From 4104d7fa4f1a93dfb7e3c53b25327dc271e7c1d4 Mon Sep 17 00:00:00 2001 From: Abhas Date: Thu, 20 Apr 2017 17:34:03 +0530 Subject: [PATCH] Add fc files and change to export mode --- fusioncharts.charts.js | 11129 ++++ fusioncharts.gantt.js | 5472 ++ fusioncharts.jqueryplugin.js | 1486 + fusioncharts.js | 89297 +++++++++++++++++++++++++++ fusioncharts.maps.js | 8990 +++ fusioncharts.msstackedcombidy2d.js | 916 + fusioncharts.powercharts.js | 31291 ++++++++++ fusioncharts.ssgrid.js | 822 + fusioncharts.treemap.js | 5315 ++ fusioncharts.widgets.js | 20537 ++++++ fusioncharts.zoomscatter.js | 4154 ++ index.html | 4 +- 12 files changed, 179412 insertions(+), 1 deletion(-) create mode 100644 fusioncharts.charts.js create mode 100644 fusioncharts.gantt.js create mode 100644 fusioncharts.jqueryplugin.js create mode 100644 fusioncharts.js create mode 100644 fusioncharts.maps.js create mode 100644 fusioncharts.msstackedcombidy2d.js create mode 100644 fusioncharts.powercharts.js create mode 100644 fusioncharts.ssgrid.js create mode 100644 fusioncharts.treemap.js create mode 100644 fusioncharts.widgets.js create mode 100644 fusioncharts.zoomscatter.js diff --git a/fusioncharts.charts.js b/fusioncharts.charts.js new file mode 100644 index 0000000..e5916ba --- /dev/null +++ b/fusioncharts.charts.js @@ -0,0 +1,11129 @@ + +(function (factory) { + if (typeof module === 'object' && typeof module.exports !== "undefined") { + module.exports = factory; + } else { + factory(FusionCharts); + } +}(function (FusionCharts) { + +/**! + * @license FusionCharts JavaScript Library + * Copyright FusionCharts Technologies LLP + * License Information at + */ +/** + * @private + * @module fusioncharts.renderer.javascript.charts.common + */ +/* jshint ignore: start */ +FusionCharts.register ('module', ['private', 'modules.renderer.js-charts', function () { +FusionCharts.register ('module', ['private', 'modules.renderer.js-column2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('column2d', { + standaloneInit: true, + friendlyName: 'Column Chart', + creditLabel: creditLabel, + defaultDatasetType : 'column', + applicableDSList: { 'column': true }, + singleseries: true + },chartAPI.sscartesian, { + enablemousetracking: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-column3d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('column3d', { + friendlyName: '3D Column Chart', + defaultDatasetType: 'column3d', + applicableDSList: { 'column3d': true }, + defaultPlotShadow: 1, + creditLabel: creditLabel, + is3D: true, + standaloneInit: true, + hasLegend: false, + singleseries: true, + fireGroupEvent: true, + defaultZeroPlaneHighlighted: false + }, chartAPI.sscartesian3d, { + showplotborder: 0, + enablemousetracking: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-bar2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname); + + chartAPI ('bar2d', { + friendlyName: 'Bar Chart', + isBar: true, + standaloneInit: true, + defaultDatasetType: 'bar2d', + creditLabel: creditLabel, + applicableDSList: { 'bar2d': true }, + singleseries: true, + spaceManager: chartAPI.barbase + }, chartAPI.ssbarcartesian, { + enablemousetracking: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-bar3d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('bar3d', { + friendlyName: '3D Bar Chart', + defaultDatasetType: 'bar3d', + applicableDSList: { 'bar3d': true }, + defaultPlotShadow: 1, + fireGroupEvent: true, + standaloneInit: true, + creditLabel: creditLabel, + is3D: true, + isBar: true, + singleseries: true, + defaultZeroPlaneHighlighted: false + }, chartAPI.ssbarcartesian3d, { + showplotborder: 0, + enablemousetracking: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-area2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('area2d', { + friendlyName: 'Area Chart', + standaloneInit: true, + creditLabel: creditLabel, + defaultDatasetType: 'area', + singleseries: true, + defaultPlotShadow: 0 + }, chartAPI.sscartesian, { + enablemousetracking: true + }, chartAPI.areabase); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-line', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname); + + chartAPI ('line', { + friendlyName: 'Line Chart', + standaloneInit: true, + creditLabel: creditLabel, + defaultPlotShadow: 1, + singleseries: true, + axisPaddingLeft: 0, + axisPaddingRight: 0, + defaultDatasetType: 'line' + }, chartAPI.sscartesian, { + zeroplanethickness: 1, + enablemousetracking: true, + zeroplanealpha: 40, + showzeroplaneontop: 0 + }, chartAPI.areabase); +}]); + + +FusionCharts.register ('module', ['private', 'modules.renderer.js-pareto2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI, + pluck = lib.pluck, + pluckNumber = lib.pluckNumber, + componentDispose = lib.componentDispose, + preDefStr = lib.preDefStr, + NINETYSTRING = preDefStr.NINETYSTRING; + + chartAPI ('pareto2d', { + defaultDatasetType: 'column2d', + singleseries : true, + creditLabel: creditLabel, + _createDatasets: function () { + var iapi = this, + components = iapi.components, + dataObj = iapi.jsonData, + is3d = iapi.is3D, + numberFormatter = components.numberFormatter, + dataset = dataObj.data || (dataObj.dataset && dataObj.dataset[0] && dataObj.dataset[0].data), + length = dataset && dataset.length, + chartAttr = dataObj.chart, + yAxis, + i, + datasetStore, + datasetObj, + defaultDatasetType = iapi.defaultDatasetType, + DsGroupClass, + Pareto = FusionCharts.get('component', ['dataset', 'Pareto']), + dsType, + DsClass, + GroupManager, + datasetJSON, + pareto = new Pareto (), + showCumulativeLine = pluckNumber (chartAttr.showcumulativeline, 1), + groupManagerName, + dataOnlyArr = [], + newDataLen, + oldDataLen, + datasetComponent, + TRUE_STRING = 'true', + ONE_STRING = '1', + setObj, + value; + + if (!dataset) { + iapi.setChartMessage(); + return; + } + + for (i = 0; i< length; i++) { + setObj = dataset[i]; + value = numberFormatter.getCleanValue (setObj.value); + if (value !== null && !(setObj.vline === TRUE_STRING || setObj.vline === true || + setObj.vline === 1 || setObj.vline === ONE_STRING)) { + dataOnlyArr.push (setObj); + } + } + iapi.config.categories = dataOnlyArr; + + datasetStore = components.dataset || (components.dataset = []); + + datasetJSON = dataOnlyArr; + dsType = pluck (defaultDatasetType); + dsType = dsType && dsType.toLowerCase (); + DsGroupClass = FusionCharts.register('component', ['datasetGroup', 'column']); + GroupManager = components[groupManagerName] = new DsGroupClass (); + GroupManager.chart = iapi; + GroupManager.init (); + /// get the DsClass + if (is3d) { + DsClass = FusionCharts.get('component', ['dataset', 'Column3d']); + } + else { + DsClass = FusionCharts.get('component', ['dataset', 'Column']); + } + if (DsClass) { + datasetComponent = datasetStore[0]; + if (!datasetComponent) { + // create the dataset Object + datasetObj = new DsClass (); + datasetStore.push (datasetObj); + datasetObj.chart = iapi; + datasetObj.index = i; + // add to group manager + GroupManager && GroupManager.addDataSet (datasetObj, 0, 0); + pareto.init (datasetObj, datasetJSON, defaultDatasetType); + } + else { + newDataLen = datasetJSON.length; + oldDataLen = datasetComponent.components.data.length; + if (newDataLen < oldDataLen) { + datasetComponent.removeData(newDataLen, oldDataLen - newDataLen); + } + datasetComponent.JSONData = { + data: datasetJSON + }; + pareto.configure.call(datasetComponent); + } + + } + yAxis = components.yAxis[1]; + if (showCumulativeLine) { + yAxis && yAxis.setAxisConfig({ + drawLabels: true, + drawPlotLines: true, + drawAxisName: true, + drawAxisLine: true, + drawPlotBands: true, + drawTrendLines: true, + drawTrendLabels: true + }); + yAxis.show(); + DsClass = FusionCharts.get('component', ['dataset', 'line']); + + datasetComponent = datasetStore[1]; + if (!datasetComponent) { + datasetObj = new DsClass (); + datasetStore.push (datasetObj); + datasetObj.chart = iapi; + datasetObj.index = i; + pareto.init (datasetObj, datasetJSON, 'line'); + } + else { + newDataLen = datasetJSON.length; + oldDataLen = datasetComponent.components.data.length; + if (newDataLen < oldDataLen) { + datasetComponent.removeData(newDataLen, oldDataLen - newDataLen); + } + datasetComponent.JSONData = { + data: datasetJSON + }; + pareto.configure.call(datasetComponent); + } + + } + else { + datasetComponent = datasetStore[1]; + if (datasetComponent) { + componentDispose.call(datasetComponent); + datasetStore.pop(); + } + + if (yAxis) { + yAxis.setAxisConfig({ + drawLabels: false, + drawPlotLines: false, + drawAxisName: false, + drawAxisLine: false, + drawPlotBands: false, + drawTrendLines: false, + drawTrendLabels: false + }); + yAxis.hide(); + } + } + + }, + _setCategories: function () { + + var iapi = this, + components = iapi.components, + dataObj = iapi.jsonData, + dataset = dataObj.dataset, + numberFormatter = components.numberFormatter, + xAxis = components.xAxis, + categories = dataObj.data || (dataset && dataset[0].data) || [], + catArr = [], + catObj, + catLen = categories.length, + i, + vLine = {}, + count = 0, + value; + + for (i = 0; i < catLen; i++) { + catObj = categories[i]; + value = numberFormatter.getCleanValue (catObj.value, true); + + if (catObj.vline === 'true' || catObj.vline === '1' || catObj.vline === 1 || + catObj.vline === true) { + vLine[count] = catObj; + } + else { + if (value === null) { + continue; + } + else { + catObj.value = value; + catArr.push (catObj); + } + } + count++; + } + + catArr.sort (function (a, b) { + return b.value - a.value; + }); + + for (i in vLine) { + catArr.splice(i, 0, vLine[i]); + } + xAxis[0].setCategory (catArr); + }, + standaloneInit: true, + hasLegend: false, + isPercentage: true + }, chartAPI.msdybasecartesian, { + enablemousetracking: true, + plotfillalpha: NINETYSTRING + }); + + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-pareto3d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI, + preDefStr = lib.preDefStr, + NINETYSTRING = preDefStr.NINETYSTRING; + + chartAPI ('pareto3d', { + standaloneInit: true, + is3D: true, + friendlyName: '3D Pareto Chart', + creditLabel: creditLabel, + fireGroupEvent: true, + defaultPlotShadow: 1, + singleseries : true, + hasLegend: false, + defaultDatasetType: 'column3d', + _createDatasets: chartAPI.pareto2d, + _setCategories: chartAPI.pareto2d, + isPercentage: true + }, chartAPI.msdybasecartesian3d, { + plotfillalpha: NINETYSTRING, + use3dlineshift: 1, + enablemousetracking: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-pie2d', function () { + var global = this, + lib = global.hcLib, + hasTouch = lib.hasTouch, + win = global.window, + doc = win.document, + //strings + BLANKSTRING = lib.BLANKSTRING, + //add the tools thats are requared + pluck = lib.pluck, + pluckNumber = lib.pluckNumber, + toRaphaelColor = lib.toRaphaelColor, + supportsTouch = 'createTouch' in doc, + // The devices which both touch and pointer. + supportsOnlyTouch = (supportsTouch && + ! (win.navigator.maxTouchPoints || + win.navigator.msMaxTouchPoints)), + ROLLOVER = 'DataPlotRollOver', + ROLLOUT = 'DataPlotRollOut', + EVENTARGS = 'eventArgs', + each = lib.each, + TRANSFORMSTR = 't', + plotEventHandler = lib.plotEventHandler, + schedular = lib.schedular, + priorityList = lib.priorityList, + /* + * Function to get the exact visual position (top, left) of a element + */ + getContainerPosition = function (el, noscroll) { + var p = { + left: 0, + top: 0 + }, + clientRect; + // for newer browsers + if (el.getBoundingClientRect) { + clientRect = el.getBoundingClientRect (); + p.top = clientRect.top + (win.pageYOffset || doc.scrollTop || 0) - (doc.clientTop || 0); + p.left = clientRect.left + (win.pageXOffset || doc.scrollLeft || 0) - (doc.clientLeft || 0); + + } + // for very older browsers + else { + // iterate till the root element of the DOM to get the exact position of the element + while (el) { + p.left += (el.offsetLeft || 0); + p.top += (el.offsetTop || 0); + if (el !== doc.body && el !== doc.documentElement && !noscroll) { + p.left -= (el.scrollLeft || 0); + p.top -= (el.scrollTop || 0); + } + el = el.offsetParent; + } + } + return p; + }, + docMode8 = win.document.documentMode === 8, + UNDEFINED, + HIDDEN = 'hidden', + VISIBLE = docMode8 ? 'visible' : '', + M = 'M', + L = 'L', + math = Math, + mathSin = math.sin, + mathCos = math.cos, + mathRound = math.round, + mathATan2 = math.atan2, + mathMin = math.min, + mathMax = math.max, + mathAbs = math.abs, + mathPI = math.PI, + mathCeil = math.ceil, + mathFloor = math.floor, + deg2rad = mathPI / 180, + rad2deg = 180 / mathPI, + pi = Math.PI, + piBy2 = pi / 2, + pi2 = 2 * pi, + pi3By2 = pi + piBy2, + convertColor = lib.graphics.convertColor, + // COLOR_TRANSPARENT = lib.COLOR_TRANSPARENT, + POSITION_BOTTOM = lib.POSITION_BOTTOM, + POSITION_RIGHT = lib.POSITION_RIGHT, + chartAPI = lib.chartAPI, + COMMASTRING = lib.COMMASTRING, + ZEROSTRING = lib.ZEROSTRING, + ONESTRING = lib.ONESTRING, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + getClickArcTangent = function (x, y, center, ref, pieYScale) { + return mathATan2 ((y - center[1] - ref.top) / pieYScale, x - center[0] - ref.left); + }; + + chartAPI ('pie2d', { + friendlyName: 'Pie Chart', + standaloneInit: true, + defaultSeriesType: 'pie', + defaultPlotShadow: 1, + reverseLegend: 1, + alignCaptionWithCanvas: 0, + sliceOnLegendClick: true, + isSingleSeries: true, + dontShowLegendByDefault: true, + defaultDatasetType : 'Pie2D', + applicableDSList: { + 'Pie2D': true + }, + defaultZeroPlaneHighlighted: false, + creditLabel: creditLabel, + _plotDragMove: function (dx, dy, x, y, evt) { + var o = this, + plotItem = o.data ('plotItem'), + chart = plotItem.chart, + seriesData = plotItem.seriesData, + datasets = chart.components.dataset, + datasetConf = datasets[0].config, + angle; + + if (isNaN(dx) || isNaN(dy) || (!datasetConf.enableRotation || seriesData.singletonCase) || + seriesData.isRightClicked) { + return; + } + angle = getClickArcTangent.call (evt, x, y, seriesData.pieCenter, + seriesData.chartPosition, 1); + if (!seriesData.isRotating) { + seriesData.dragStartAngle !== angle && (seriesData.isRotating = true); + /** + * This event is fired on drag rotation start of pie chart. + * + * @event FusionCharts#rotationStart + * @group chart:pie + * + * @param { number} startingAngle - Gives the value of the + * startingAngle of the chart, when the chart + * starts rotating + */ + global.raiseEvent ('RotationStart', { + startingAngle: (seriesData._rotationalStartAngle = chart._startingAngle()) + }, + chart.chartInstance + ); + } + + datasetConf.startAngle += (angle - seriesData.dragStartAngle); + seriesData.dragStartAngle = angle; + seriesData.moveDuration = 0; + + // Check if already there is an initiation for visual updates. + if (!datasetConf.updateInited) { + // set the flag to truth value. + datasetConf.updateInited = true; + setTimeout(chart._batchRotate || (chart._batchRotate = function () { + // visually update the chart + chart._rotate (); + // turn the flag to falsy after an interval + datasetConf.updateInited = false; + }), 50); + } + }, + _plotDragStart: function (x, y, evt) { + var o = this, + plotItem = o.data ('plotItem'), + chart = plotItem.chart, + seriesData = plotItem.seriesData, + datasets = chart.components.dataset, + datasetConf = datasets[0].config, + startingAng = -datasetConf.startAngle, + angle; + seriesData.isRightClicked = !hasTouch && (evt.button !== 0 && evt.button !== 1) ? true : false; + + if (!datasetConf.enableRotation || seriesData.isRightClicked) { + return; + } + seriesData.isRotating = false; + //Calculate chart's current position. Chart cotainer may be moved mean while + seriesData.chartPosition = getContainerPosition (chart.linkedItems.container); + angle = getClickArcTangent.call (evt, x, y, seriesData.pieCenter, + seriesData.chartPosition, 1); + seriesData.dragStartAngle = angle; + seriesData.startingAngleOnDragStart = startingAng; + }, + _plotDragEnd: function (e) { + var o = this, + rotaionalEndAngle, + plotItem = o.data ('plotItem'), + chart = plotItem.chart, + chartConfig = chart.config, + seriesData = plotItem.seriesData; + if (seriesData.isRightClicked) { + return; + } + + chartConfig.clicked = true; + if (!chart.disposed) { + /*extend2 (chart.logic.chartInstance.jsVars._reflowData, + reflowUpdate, true);*/ + chart._rotate (); + } + + !seriesData.isRotating && chart._plotGraphicClick.call (plotItem.graphic, e); + + delete chartConfig.clicked; + if (seriesData.isRotating) { + /* The events mouseup, dragend and click are raised in order. In order + * to update the flag isRotating to false post click event, setTimeout + * called, to take immediate effect, is programmed to update the flag. + * Thus, the flag gets updated post the series of events, in effect. + * NB: Click event is subscribed conditionally. + */ + setTimeout (function () { + seriesData.isRotating = false; + }, 0); + /** + * This event is fired on drag rotation end of pie chart. + * + * @event FusionCharts#rotationEnd + * @group chart:pie + * + * @param { number} changeInAngle - Gives the value by how much + * the chart was rotated + * @param { number} startingAngle - Gives the value of the + * startingAngle of the chart on rotation end. + */ + global.raiseEvent ('RotationEnd', { + startingAngle: (rotaionalEndAngle = chart._startingAngle()), + changeInAngle: (rotaionalEndAngle - seriesData._rotationalStartAngle) + }, chart.chartInstance); + } + // !chart.isHovered && chart.onPlotHover (o, false); + }, + _plotRollOver: function (e) { + var ele = this, + plotItem = ele.plotItem || ele.data ('plotItem'), + chart = plotItem.chart, + conf = chart.components.dataset[0].config, + innerDiameter, + centerLabelConfig, + centerLabelText; + if (!conf.isRotating) { + plotEventHandler.call (ele, chart, e, ROLLOVER); + chart.onPlotHover (ele, true); + } + conf.isHovered = true; + if (innerDiameter = plotItem.innerDiameter) { + (centerLabelConfig = plotItem.centerLabelConfig) && + (centerLabelText = centerLabelConfig.label) && + chart.drawDoughnutCenterLabel (centerLabelText, plotItem.center[0], + plotItem.center[1], innerDiameter, innerDiameter, centerLabelConfig, false); + } + }, + onPlotHover: function (plot, hover) { + var plotItem = plot.data ('plotItem'), + center = plotItem.center, + rolloverProps = plotItem.rolloverProperties || {}, + color = hover ? rolloverProps.color : plotItem.color, + borderWidth = hover ? rolloverProps.borderWidth : plotItem.borderWidth, + borderColor = hover ? rolloverProps.borderColor : plotItem.borderColor; + if (color) { + if (hover) { + color.cx = center[0]; + color.cy = center[1]; + color.r = plotItem.radius; + } + rolloverProps.enabled && plotItem.graphic.attr ( { + fill: toRaphaelColor(color), + 'stroke-width': borderWidth, + 'stroke': borderColor + }); + } + }, + + _plotRollOut: function (e) { + var ele = this, + plotItem = ele.plotItem || ele.data ('plotItem'), + chart = plotItem.chart, + seriesData = chart.components.dataset[0], + conf = seriesData.config, + /** @todo: enable doughnut center label feature related to enableMultiSlicing */ + enableMultiSlicing = true, //seriesData.enableMultiSlicing, + innerDiameter, + centerLabelConfig, + centerLabelText; + if (!conf.isRotating) { + plotEventHandler.call (ele, chart, e, ROLLOUT); + chart.onPlotHover (ele, false); + } + conf.isHovered = false; + if (innerDiameter = plotItem.innerDiameter) { + (centerLabelConfig = !enableMultiSlicing ? + conf.lastCenterLabelConfig : conf.centerLabelConfig) && + ((centerLabelText = centerLabelConfig.label) || !centerLabelText) && + chart.drawDoughnutCenterLabel (centerLabelText, plotItem.center[0], + plotItem.center[1], innerDiameter, innerDiameter, centerLabelConfig, false); + } + }, + _rotate: function () { + var _ringPath, + _rotateAttrs, + chart = this, + dataOptions = chart.components.dataset[0], + conf = dataOptions.config, + plotData = dataOptions.components.data, + chartConfig = chart.config, + piePlotOptions = conf.piePlotOptions, + startAngle = (conf.startAngle || 0) % pi2, + slicedOffset = conf.slicingDistance, + seriesConf = dataOptions.config, + valueTotal = seriesConf.valueTotal, + factor = pi2 / valueTotal, + cx = chartConfig.canvasLeft + chartConfig.canvasWidth * 0.5, + cy = chartConfig.canvasTop + chartConfig.canvasHeight * 0.5, + r = conf.pieMinRadius, + r2 = (piePlotOptions.innerSize || 0) * 0.5, + val, + angle, + angle1, + angle2, + setConfig, + setGraphics, + i; + + angle1 = angle2 = startAngle; + for (i = 0; i < plotData.length; i += 1) { + setConfig = plotData[i].config; + setGraphics = plotData[i].graphics; + val = setConfig.y; + + if (val === null || val === undefined) { + continue; + } + + // plotItem = plotItems[i]; + + angle2 = angle1; + // This conditional assignment of value 2 * pi is to by-pass a + // computational error inherent to any computer system, which + // happens here for certain values in singleton cases. + angle1 -= !seriesConf.singletonCase ? val * factor : pi2; + angle = (angle1 + angle2) * 0.5; + + setConfig.angle = angle; + setConfig.transX = mathCos (angle) * slicedOffset; + setConfig.transY = mathSin (angle) * slicedOffset; + + setConfig.slicedTranslation = TRANSFORMSTR + + (mathCos (angle) * slicedOffset) + COMMASTRING + + (mathSin (angle) * slicedOffset); + + if (!(_rotateAttrs = setConfig._rotateAttrs)) { + _rotateAttrs = setConfig._rotateAttrs = { + ringpath: [], + transform: BLANKSTRING + }; + } + _ringPath = _rotateAttrs.ringpath; + _ringPath[0] = cx; + _ringPath[1] = cy; + _ringPath[2] = r; + _ringPath[3] = r2; + _ringPath[4] = angle1; + _ringPath[5] = angle2; + _rotateAttrs.transform = setConfig.sliced ? setConfig.slicedTranslation : BLANKSTRING; + setGraphics.element.attr (_rotateAttrs); + } + chart.placeDataLabels (true, plotData, conf); + }, + getPlotData: function (id, slicedState) { + var chart = this, + dataset = chart.components.dataset[0], + data = dataset.components.data[id].config, + userData = dataset.config.userData || (dataset.config.userData = []), + plotData, + value, + prop; + + if (!userData[id]) { + plotData = userData[id] = {}; + for (prop in data) { + typeof (value = data[prop]) !== 'object' && + typeof (value) !== 'function' && + prop.indexOf('_') !== 0 && (plotData[prop] = value); + } + plotData.value = plotData.y; + plotData.categoryLabel = plotData.label = plotData.seriesName; + + delete plotData.y; + delete plotData.total; + delete plotData.doNotSlice; + delete plotData.name; + delete plotData.seriesName; + delete plotData.centerAngle; + delete plotData.showInLegend; + delete plotData.angle; + delete plotData.endAngle; + delete plotData.isVisible; + delete plotData.setColor; + delete plotData.slicedTranslation; + delete plotData.startAngle; + delete plotData.transX; + delete plotData.transY; + delete plotData.pValue; + } + else { + plotData = userData[id]; + } + + plotData.sliced = slicedState; + return plotData; + }, + _plotGraphicClick: function (evt) { + var prevTextPos, + o = this.element || this, + plotItem = o.plotItem || o.data ('plotItem'), + elData = o.data('eventArgs') || {}, + chart = plotItem.chart, + index = plotItem.index, + // elData = o.data ('eventArgs') || { }, + chartSeriesData = chart.components.dataset[0], + seriesData = chartSeriesData.config, + enableMultiSlicing = seriesData.enableMultiSlicing, + set = chartSeriesData.components.data[index], + setGraphics = set.graphics, + setConfig = set.config, + doNotSlice = setConfig.doNotSlice, + atleastOneOtherSliced, + graphic, + dataLabel, + sliced, + slicedTranslation = setConfig.slicedTranslation, + transX, + transY, + connector, + connectorPath, + lastSliceTimeStamp, + minTimeForNextSlice = 400, + animationObj = chart.get('config', 'animationObj'), + animationDuration = animationObj.duration || 200, + mainElm = animationObj.dummyObj, + animObj = animationObj.animObj, + animType = animationObj.animType, + eventArgs, + path; + + !seriesData.isRotating && plotEventHandler.call (o, chart, evt); + + if (seriesData.isRotating || seriesData.singletonCase || doNotSlice) { + return; + } + + // If 'enableMultiSlicing' is false, sliceInOtherPies is called only once + // for the clicked pie. From within sliceInOtherPies, 'enableMultiSlicing' + // is set to true, method plotGraphicClick is called once for each + // sliced pie and finally, value of 'enableMultiSlicing' is reset to false. + // Value of multiSliced is always false for plotGraphicClick call from + // sliceInOtherPies. However, its value may be true or false, by the + // return value of the sliceInOtherPies. + atleastOneOtherSliced = !enableMultiSlicing && chart.sliceInOtherPies (index); + + if ((sliced = setConfig.sliced) && atleastOneOtherSliced) { + return; + } + + // + // In chrome of hybrid touch devices (touch + mouse), slice is getting fired twice for single touch + // To prevent this cancle slice event within very sort period + // + if (supportsTouch && !supportsOnlyTouch) { + lastSliceTimeStamp = new Date ().getTime (); + if (plotItem.lastSliceTimeStamp && + (lastSliceTimeStamp - plotItem.lastSliceTimeStamp) < minTimeForNextSlice) { + return; + } + + // store current time stamp + plotItem.lastSliceTimeStamp = lastSliceTimeStamp; + } + graphic = setGraphics.element; + connector = setGraphics.connector; + dataLabel = setGraphics.label || setGraphics.dataLabel; + slicedTranslation = typeof slicedTranslation === 'object' ? 't' + slicedTranslation : slicedTranslation; + connectorPath = setConfig.connectorPath; + transX = (sliced ? -1 : 1) * setConfig.transX; + transY = (sliced ? -1 : 1) * setConfig.transY; + eventArgs = graphic.data(EVENTARGS) || graphic.data(EVENTARGS, {}); + /** + * This event is fired when a pieSlice in pie-chart starts slicing transition. + * + * @event FusionCharts#slicingStart + * @group chart:pie-slice + * + * @param { object} data - Contains the values for the following + * attributes borderColor, borderWidth, + * categoryLabel, dashStyle, displayValue, + * hoverEffects (boolean), label, link, + * rolloverProperties (boolean), sliced (boolean), + * toolText, value (value of the sliced object) + * @param { boolean} slicedState - Tells the state of the slice before + * transition begins. The value is true for + * sliced-out state and false for sliced-in state. + */ + global.raiseEvent ('slicingStart', { + slicedState: sliced, + dataIndex: ('index' in elData) && elData.index, + data: chart.getPlotData (index, sliced) + }, chart.chartInstance); + + graphic.animateWith(mainElm, animObj, { + transform: sliced ? 't0,0' : slicedTranslation + }, animationDuration, animType, function () { + /** + * This event is fired when a pieSlice in pie-chart ends slicing transition. + * + * @event FusionCharts#slicingEnd + * @group chart:pie-slice + * + * @param { object} data - Contains the values for the following + * attributes borderColor, borderWidth, + * categoryLabel, dashStyle, displayValue, + * hoverEffects (boolean), label, link, + * rolloverProperties (boolean), sliced (boolean), + * toolText, value (value of the sliced object) + * @param { boolean} slicedState - Tells the state of the slice + * before transition begins. The value is true + * for sliced-out state and false for + * sliced-in state. + */ + global.raiseEvent ('slicingEnd', { + slicedState: sliced, + dataIndex: ('index' in elData) && elData.index, + data: chart.getPlotData (index, sliced) + }, chart.chartInstance); + }); + + if (dataLabel && dataLabel.x) { + if (!(prevTextPos = dataLabel.data('textPos'))) { + prevTextPos = dataLabel.data('textPos', { + x: dataLabel.x, + y: dataLabel.y + }); + } + dataLabel.animateWith(mainElm, animObj, { + x: dataLabel.x + (sliced ? 0 : transX) + }, animationDuration, animType); + prevTextPos.x = dataLabel.x + (sliced ? 0 : transX); + } + + if (connectorPath) { + path = connectorPath.slice(0); + path[1] += transX; + path[2] += transY; + path[4] += transX; + path[6] += transX; + connector.animateWith(mainElm, animObj, { + path: path + }, animationDuration, animType); + setConfig.connectorPath = path; + } + // updating the slicing information. + eventArgs.isSliced = (sliced = setConfig.sliced = !sliced); + + /** @todo: Enable center label feature related to enableMultiSlicing */ + //if ((innerDiameter = plotItem.innerDiameter) && !enableMultiSlicing) { + // (centerLabelConfig = sliced && plotItem.centerLabelConfig.label ? + // plotItem.centerLabelConfig : chartSeriesData.centerLabelConfig) && + // (centerLabelText = centerLabelConfig.label) && + // chart.drawDoughnutCenterLabel (centerLabelText, plotItem.center[0], plotItem.center[1], + // innerDiameter, innerDiameter, centerLabelConfig, true); + //} + + return sliced; + }, + sliceInOtherPies: function (mainPieId) { + var chart = this, + series = chart.components.dataset[0], + plotItems = series.components.data, + i = plotItems.length, + numSliced = 0, + plot; + + series.enableMultiSlicing = true; + + while (i--) { + i !== mainPieId && (plot = plotItems[i]).config.sliced && ++numSliced && + chart._plotGraphicClick.call (plot.graphics); + } + + series.enableMultiSlicing = false; + // Returns true when at least one other pie is sliced + return !!numSliced; + }, + placeDataLabels: (function () { + /* + * Pie Helper Functions. + */ + + var sortArrayByPoint = function (a, b) { + return a.point.value - b.point.value; + }, + sortArrayByAngle = function (a, b) { + return a.angle - b.angle; + }, + alignments = ['start', 'start', 'end', 'end'], + alignCenter = 'middle', + ySign = [-1, 1, 1, -1], + xSign = [1, 1, -1, -1]; + + return function (isRotating, plotItems, plot, seriesData) { + var chart = this, + chartConfig = chart.config, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + legendConfig = chartComponents.legend.config, + legendWidth = legendConfig.symbolWidth, + legendAlign = legendConfig.align, + legendBorderwidth = legendConfig.borderWidth, + datasetGraphics = dataSet.graphics, + plotOptions = dataSet.config, + canvasLeft = chartConfig.canvasLeft, + canvasTop = chartConfig.canvasTop, + canvasWidth = chartConfig.canvasWidth, + cx = canvasLeft + chartConfig.canvasWidth * 0.5, + cy = canvasTop + chartConfig.canvasHeight * 0.5, + smartLabel = chart.linkedItems.smartLabel, + dataLabelsOptions = plotOptions.dataLabelOptions, + style = dataLabelsOptions.style, + lineHeight = pluckNumber (mathCeil (parseFloat (style.lineHeight)), 12), + placeInside = (plotItems.length === 1 ? chartConfig.singletonPlaceValue : false), + skipOverlapLabels = dataLabelsOptions.skipOverlapLabels, + manageLabelOverflow = dataLabelsOptions.manageLabelOverflow, + connectorPadding = dataLabelsOptions.connectorPadding, + distanceOption = dataLabelsOptions.distance, + remainingHeight, + seriesCenter = seriesData && seriesData.metrics || [ + cx, + cy, + 2 * plotOptions.pieMinRadius, + (plotOptions.innerSize || 0) + ], + centerY = seriesCenter[1], + centerX = seriesCenter[0], + radius = seriesCenter[2] * 0.5, + // divide the points into quarters for anti collision + quarters = [ + [], // top right + [], // bottom right + [], // bottom left + [] // top left + ], + plotLeft = canvasLeft, + plotTop = canvasTop, + plotWidth = canvasWidth, + dataLabelsRadius = (plot.labelsRadius = radius + distanceOption), + labelFontSize = parseInt (style.fontSize, 10), + labelHeight = labelFontSize, + halfLabelHeight = labelHeight / 2, + xDisplacement = [connectorPadding, + connectorPadding, -connectorPadding, -connectorPadding + ], + maxLabels = (plot.labelsMaxInQuadrant = mathFloor ( + dataLabelsRadius / labelHeight)), + isSmartLineSlanted = dataLabelsOptions.isSmartLineSlanted, + innerRadius = seriesCenter[3] / 2, + align, + i, + labelWidth, + j, + oriY, + maxYmayHave, + spaceRequired, + length, + k, + sliced, + x1, + x2, + x3, + x4, + y1, + y2, + y3, + points, + point, + angle, + connector, + connectorPath, + excess, + excessArr, + labelQuadrantHeight, + maxQuadrantLabel, + dataLabel, + quarter, + transX, + transY, + smartLabelObj, + slicedTranslation, + centerDistance, + sameSideQuadrentLastY = Number.POSITIVE_INFINITY, + extraLineSpaceComp, + lPoint, + pointConfig, + pointGraphics, + leftClubbedQuadrent = [], + rightClubbedQuadrent = [], + prevTextPos, + prevConnectorPos, + animationObj = chart.get('config', 'animationObj'), + // animating on every rotations slows the performance. @revisit. + animationDuration = isRotating ? 0 : (animationObj.duration || 0), + mainElm = animationObj.dummyObj, + animObj = animationObj.animObj, + animType = animationObj.animType, + + plotDragMove = chart._plotDragMove, + plotDragStart = chart._plotDragStart, + plotDragEnd = chart._plotDragEnd, + plotRollOver = chart._plotRollOver, + plotRollOut = chart._plotRollOut, + paper = chartComponents.paper, + dataLabelContainer = datasetGraphics.dataLabelContainer, + _textAttrs, + _textCss; + + // run parent method + /**^ + * Introduced the isRotating argument too to detect + * whether this is called during rotation. + */ + smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow); + if (!isRotating) { + //do not set the style every time + // Do it for first time + smartLabel.setStyle (style); + } + + // arrange points for detection collision + // Creates an array of quarter containing labels of each + // quarter if there has only one label the draw it inside + if (plotItems.length == 1 && !innerRadius && placeInside) { + point = plotItems[0]; + if ((_textAttrs = point.config._textAttrs).text) { + pointGraphics = point.graphics; + pointConfig = point.config; + dataLabel = pointGraphics.label; + + point.slicedTranslation = [plotLeft, plotTop]; + _textAttrs['text-anchor'] = alignCenter; + _textAttrs.x = 0; + _textAttrs.y = 0; + _textAttrs.transform = ['t', centerX, centerY]; + + if (!dataLabel) { + dataLabel = pointGraphics.label = paper + .text(_textAttrs, _textCss, dataLabelContainer) + .drag(plotDragMove, plotDragStart, plotDragEnd) + .hover(plotRollOver, plotRollOut); + } + else { + dataLabel.animateWith (mainElm, animObj, _textAttrs, animationDuration, animType); + } + + dataLabel.x = centerX; + dataLabel.data('textPos', { + x: centerX, + y: centerY + }) + .data('plotItem', _textAttrs.plotItem) + .data(EVENTARGS, _textAttrs.eventArgs); + + if (pointConfig.y !== null && pointConfig.y !== undefined) { + dataLabel.show (); + } + + if (pointGraphics.connector) { + pointGraphics.connector.attr({ + path: [] + }); + } + } + } + else { + if (placeInside) { + centerDistance = innerRadius + ((radius - innerRadius) / 2); + each (plotItems, function (point) { + pointConfig = point.config; + if ((_textAttrs = pointConfig._textAttrs).text) { + pointGraphics = point.graphics; + dataLabel = pointGraphics.label; + if (pointConfig.y !== null && pointConfig.y !== undefined) { + connector = pointGraphics.connector; + connector && connector.show(); + dataLabel && dataLabel.show (); + } + _textAttrs.transform = 't0,0'; + angle = pointConfig.angle; + y3 = centerY + (centerDistance * + mathSin (angle)); + //previous implementation adds halfLebelHeight - 2 which makes the + //label aligned to top instead middle [RED - 1717] + /*y3 = centerY + (centerDistance * + mathSin (angle)) + halfLabelHeight - 2;*/ + x3 = centerX + (centerDistance * mathCos (angle)); + _textAttrs._x = x3; + _textAttrs._y = y3; + if (point.sliced) { + slicedTranslation = point.slicedTranslation; + transX = slicedTranslation[0] - plotLeft; + transY = slicedTranslation[1] - plotTop; + x3 = x3 + transX; + y3 = y3 + transY; + } + + _textAttrs['text-anchor'] = alignCenter; + _textAttrs.x = 0; + _textAttrs.y = 0; + _textAttrs.transform = ['t', x3, y3]; + + if (!dataLabel) { + dataLabel = pointGraphics.label = paper + .text(_textAttrs, _textCss, dataLabelContainer) + .drag(plotDragMove, plotDragStart, plotDragEnd) + .hover(plotRollOver, plotRollOut); + } + else { + dataLabel.animateWith (mainElm, animObj, _textAttrs, animationDuration, + animType); + } + dataLabel.x = _textAttrs._x; + // storing original x value + // to use while slicing in (IE Issue original + // x get changed form animate) + dataLabel.x = _textAttrs._x; + dataLabel.y = _textAttrs._y; + + dataLabel + .data('plotItem', _textAttrs.plotItem) + .data(EVENTARGS, _textAttrs.eventArgs); + + if (_textAttrs.visibility === VISIBLE) { + dataLabel.show(); + } + } + + }); + } + else { //outside + for (i = plotItems.length - 1; i >= 0; i --) { + point = plotItems[i]; + pointConfig = point.config; + _textAttrs = pointConfig._textAttrs; + if (!(_textAttrs.text = pointConfig.displayValue)) { + continue; + } + pointGraphics = point.graphics; + if (pointConfig.y !== null && pointConfig.y !== undefined) { + dataLabel = pointGraphics.label; + connector = pointGraphics.connector; + connector && connector.show(); + dataLabel && dataLabel.show (); + } + _textAttrs.text = pointConfig.displayValue; + _textAttrs.transform = 't0,0'; + angle = pointConfig.angle % pi2; + + if (angle < 0) { + angle = pi2 + angle; + } + // Calculate bottom right quarter labels + if (angle >= 0 && angle < piBy2) { + quarter = 1; + } else + // Calculate bottom left quarter labels + if (angle < pi) { + quarter = 2; + } else + // Calculate top left quarter labels + if (angle < pi3By2) { + quarter = 3; + } + // Calculate top right quarter labels + else { + quarter = 0; + } + // Now put labels according to each quarter + quarters[quarter].push ( { + point: point, + angle: angle + }); + } + i = k = 4; + //if excess then remove the low value slice first + while (i--) { + if (skipOverlapLabels) { + // Find labels can fit into the quarters or not + excess = quarters[i].length - maxLabels; + if (excess > 0) { + // sort by point.value + quarters[i].sort (sortArrayByPoint); + // remove extra data form the array + // which labels can not be fitted into + // the quarters + excessArr = quarters[i].splice (0, excess); + //hide all removed labels + for (j = 0, length = excessArr.length; j < length; j += 1) { + point = excessArr[j].point; + _textAttrs = point.config._textAttrs; + pointGraphics = point.graphics; + if (pointGraphics.label) { + pointGraphics.label.attr('visibility', HIDDEN); + } + if (pointGraphics.connector) { + pointGraphics.connector.attr ( { + visibility: HIDDEN + }); + } + } + } + } + // now we sort the data labels by its label angle + quarters[i].sort (sortArrayByAngle); + } + + maxQuadrantLabel = mathMax ( + quarters[0].length, + quarters[1].length, + quarters[2].length, + quarters[3].length + ); + labelQuadrantHeight = mathMax ( + mathMin (maxQuadrantLabel, maxLabels) * labelHeight, + dataLabelsRadius + labelHeight + ); + + // Club the first and second quadrent as they appear in the right side of the + // chart and club the other two. Clubbing here means merge all the points together + // so that marginal quadrent case are handled like one point at first quadrent and + // second point at another quadrent but placed closely. + rightClubbedQuadrent = quarters[0].concat (quarters[1]); + leftClubbedQuadrent = quarters[2].concat (quarters[3]); + + for (i = rightClubbedQuadrent.length - 1; i >= 0; i--) { + lPoint = rightClubbedQuadrent[i].point.config; + // Clear if any previous values are stored + delete lPoint.clearance; + delete lPoint.clearanceShift; + oriY = mathAbs (labelQuadrantHeight * mathSin (lPoint.angle)); + if (Math.abs (sameSideQuadrentLastY - oriY) < lineHeight * 2) { + lPoint.clearance = 0; + rightClubbedQuadrent[i+1].point.clearanceShift = lineHeight / 2; + } + sameSideQuadrentLastY = oriY; + } + + sameSideQuadrentLastY = Number.POSITIVE_INFINITY; + + for (i = 0, length = leftClubbedQuadrent.length; i < length; i++) { + lPoint = leftClubbedQuadrent[i].point.config; + // Clear if any previous values are stored + delete lPoint.clearance; + delete lPoint.clearanceShift; + oriY = mathAbs (labelQuadrantHeight * mathSin (lPoint.angle)); + if (Math.abs (sameSideQuadrentLastY - oriY) < lineHeight * 2) { + lPoint.clearance = 0; + leftClubbedQuadrent[i-1].point.clearanceShift = lineHeight / 2; + } + sameSideQuadrentLastY = oriY; + } + + // reverse 1st and 3rd quardent points + quarters[1].reverse (); + quarters[3].reverse (); + while (k--) { + points = quarters[k]; + length = points.length; + + if (!skipOverlapLabels) { + if (length > maxLabels) { + labelHeight = labelQuadrantHeight / length; + } + else { + labelHeight = labelFontSize; + } + halfLabelHeight = labelHeight / 2; + } + + //1st pass + //place all labels at 1st quarter + + // calculate the total available space to put labels + spaceRequired = length * labelHeight; + // calculate the remaining height + remainingHeight = labelQuadrantHeight; + //place all child point + for (i = 0; i < length; i += 1, spaceRequired -= labelHeight) { + // Get the y position of the label (radius + // where data label is to draw) + oriY = mathAbs (labelQuadrantHeight * mathSin (points[i].angle)); + + if (remainingHeight < oriY) { + oriY = remainingHeight; + } + else if (oriY < spaceRequired) { + oriY = spaceRequired; + } + + remainingHeight = (points[i].oriY = oriY) - labelHeight; + } + + //2nd pass (reverse) + align = alignments[k]; + //place all labels at 1st quarter + maxYmayHave = labelQuadrantHeight - ((length - 1) * labelHeight); + remainingHeight = 0; + + //place all child point + for (i = points.length - 1; i >= 0; i -= 1, maxYmayHave += labelHeight) { + point = points[i].point; + angle = points[i].angle; + pointConfig = point.config; + _textAttrs = pointConfig._textAttrs; + if (!_textAttrs.text) { + continue; + } + _textCss = pointConfig._textCss; + pointGraphics = point.graphics; + sliced = pointConfig.sliced; + dataLabel = pointGraphics.label; + + oriY = mathAbs (labelQuadrantHeight * mathSin (angle)); + + if (oriY < remainingHeight) { + oriY = remainingHeight; + } + else if (oriY > maxYmayHave) { + oriY = maxYmayHave; + } + + remainingHeight = oriY + labelHeight; + + // If the there is a label down underneath or + // bordered put elipses else wrap the word. + extraLineSpaceComp = pointConfig.clearance === undefined? + mathCeil (pluckNumber (parseFloat (pointConfig.style.border), 12), 12) * 2: + mathCeil (pluckNumber (parseFloat (pointConfig.style.border), + pointConfig.clearance)) * 2; + + + + y1 = ((oriY + points[i].oriY) / 2); + x1 = centerX + xSign[k] * dataLabelsRadius * + mathCos (math.asin (y1 / labelQuadrantHeight)); + y1 *= ySign[k]; + y1 += centerY; + + y2 = centerY + (radius * mathSin (angle)); + x2 = centerX + (radius * mathCos (angle)); + + // Relation: centerX <= connectorStartX <= connectorEndX + // (for right half and vice versa for left half) + (k < 2 && x1 < x2 || k > 1 && x1 > x2) && (x1 = x2); + + x3 = x1 + xDisplacement[k]; + y3 = y1 - halfLabelHeight - 2; + x4 = x3 + xDisplacement[k]; + + _textAttrs._x = x4; + + if (manageLabelOverflow) { + labelWidth = k > 1 ? x4 - chartConfig.canvasLeft : chartConfig.canvasLeft + + plotWidth - x4; + smartLabel.setStyle (pointConfig.style); + lineHeight = pluckNumber (mathCeil (parseFloat (pointConfig.style.lineHeight)), + 12) + extraLineSpaceComp; + if (legendAlign === 'right' && pointConfig.transX > 0) { + labelWidth -= legendWidth + (legendBorderwidth * 2); + } + smartLabelObj = + smartLabel.getSmartText (pointConfig.displayValue, labelWidth, lineHeight); + + if (pointConfig.clearance === undefined && smartLabelObj.height > labelHeight) { + y1 += labelHeight; + } + + if (legendAlign !== 'right' && pointConfig.transY > 0) { + y1 -= labelHeight; + } + + _textAttrs.text = smartLabelObj.text; + // _textAttrs.title = smartLabelObj.tooltext || ''; + + _textAttrs.tooltip = smartLabelObj.tooltext; + + + /*dataLabel.attr ( { + text: smartLabelObj.text + //title: (smartLabelObj.tooltext || '') + }) + .tooltip (smartLabelObj.tooltext);*/ + } + _textAttrs._y = y3; + + if (sliced) { + transX = pointConfig.transX; + transY = pointConfig.transY; + x3 = x3 + transX; + x1 = x1 + transX; + x2 = x2 + transX; + y2 = y2 + transY; + x4 = x4 + transX; + } + + _textAttrs['text-anchor'] = align; + _textAttrs.vAlign = 'middle'; + + _textAttrs.x = x4; + _textAttrs.y = y1; + + prevTextPos = dataLabel && dataLabel.data('textPos'); + if (prevTextPos) { + dataLabel.attr({ + x: prevTextPos.x, + y: prevTextPos.y + }). animateWith(mainElm, animObj, _textAttrs, animationDuration); + } + else { + // for the initial rendering. + dataLabel = pointGraphics.label = paper + .text(_textAttrs, _textCss, dataLabelContainer) + .drag(plotDragMove, plotDragStart, plotDragEnd) + .hover(plotRollOver, plotRollOut); + } + + dataLabel.x = _textAttrs._x; + dataLabel._x = _textAttrs._x; + dataLabel.y = _textAttrs._y; + + if (_textAttrs.tooltip) { + dataLabel.tooltip(_textAttrs.tooltip); + delete _textAttrs.tooltip; + } + if (_textAttrs.visibility === VISIBLE) { + dataLabel.show(); + } + dataLabel.data('textPos', { + x: x4, + y: y1 + }) + .data('plotItem', _textAttrs.plotItem) + .data(EVENTARGS, _textAttrs.eventArgs); + // draw the connector + if ((connector = pointGraphics.connector)) { + pointConfig.connectorPath = connectorPath = [ + M, + x2, y2, // base + L, + // first break, next to the label + isSmartLineSlanted ? x1 : x2, y1, + x3, y1 // end of the string at the label + ]; + prevConnectorPos = connector.data('connectorPath'); + if (prevConnectorPos) { + if (!chartConfig.clicked) { + connector.attr({ + path: prevConnectorPos.path + }). animateWith(mainElm, animObj, { + path: connectorPath + }, animationDuration); + } + } + else { + connector.attr ( { + path: connectorPath + }); + } + connector.data('connectorPath', { + path: connectorPath + }); + } + } + } + } + } + }; + } ()), + _spaceManager: function () { + var chart = this, + chartConfig = chart.config, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + data = dataSet.components.data, + conf = dataSet.config, + legend = chartComponents.legend, + colorM = chartComponents.colorManager, + SmartLabel = chart.linkedItems.smartLabel, + textWidthArr = [], + length = conf.dataLabelCounter, + labelMaxW = 0, + fcJSONChart = chart.jsonData.chart, + manageLabelOverflow = pluckNumber (fcJSONChart.managelabeloverflow, 0), + userGivenSlicingDist = pluckNumber (fcJSONChart.slicingdistance), + slicingDistance = !conf.preSliced && (chartConfig.allPlotSliceEnabled === ZEROSTRING) && + (fcJSONChart.showlegend !== ONESTRING || fcJSONChart.interactivelegend === ZEROSTRING) ? + 0 : mathAbs (pluckNumber (userGivenSlicingDist, 20)), + pieRadius = pluckNumber (fcJSONChart.pieradius, 0), + enableSmartLabels = pluckNumber (fcJSONChart.enablesmartlabels, fcJSONChart.enablesmartlabel, 1), + skipOverlapLabels = enableSmartLabels ? pluckNumber (fcJSONChart.skipoverlaplabels, + fcJSONChart.skipoverlaplabel, 1) : 0, + isSmartLineSlanted = pluckNumber (fcJSONChart.issmartlineslanted, 1), + labelDistance = length ? pluckNumber (fcJSONChart.labeldistance, fcJSONChart.nametbdistance, 5) : + slicingDistance, + smartLabelClearance = pluckNumber (fcJSONChart.smartlabelclearance, 5), + width = chartConfig.width, + height = chartConfig.height, + actionBarHeight = (chart._manageActionBarSpace(height * 0.225) || {}).bottom, + chartWorkingWidth = width - (chartConfig.marginRight + chartConfig.marginLeft), + chartWorkingHeight = height - (chartConfig.marginTop + chartConfig.marginBottom) - + (actionBarHeight ? actionBarHeight + chartConfig.marginBottom : 0), + minOfWH = mathMin (chartWorkingHeight, chartWorkingWidth), + smartLineColor = pluck (fcJSONChart.smartlinecolor, colorM.getColor ('plotFillColor')), + smartLineAlpha = pluckNumber (fcJSONChart.smartlinealpha, 100), + smartLineThickness = pluckNumber (fcJSONChart.smartlinethickness, 0.7), + dataLabelOptions = (conf.dataLabelOptions = dataSet._parseDataLabelOptions ()), + style = dataLabelOptions.style, + lineHeight = length ? pluckNumber (parseInt (style.lineHeight, 10), 12) : 0, //2px padding + pieMinRadius = pieRadius === 0 ? minOfWH * 0.15 : pieRadius, + pieMinDia = (2 * pieMinRadius), + legendSpace = { + bottom: 0, + right: 0 + }, + captionSpace, + pieYScale = conf.pieYScale, + pieSliceDepth = conf.pieSliceDepth, + textObj, + avaiableMaxpieSliceDepth, + totalSpaceReq; + // Old code for placeValuesInside + // placeLabelsInside = pluckNumber ((FCchartName === 'doughnut2d') ? 0 : fcJSONChart.placevaluesinside), + + dataLabelOptions.connectorWidth = smartLineThickness; + dataLabelOptions.connectorPadding = pluckNumber (fcJSONChart.connectorpadding, 5); + dataLabelOptions.connectorColor = convertColor (smartLineColor, smartLineAlpha); + + // If smart label is on and there is a label defined only then modify the label distance + if (length) { + if (enableSmartLabels) { + labelDistance = smartLabelClearance; + } + labelDistance += slicingDistance; + } + // Include label + totalSpaceReq = pieMinDia + ((lineHeight + labelDistance) * 2); + + // Provide at least single line height space for caption. + // a space manager that manages the space for the tools as well as the captions. + captionSpace = chart._manageChartMenuBar(totalSpaceReq < chartWorkingHeight ? + chartWorkingHeight - totalSpaceReq : chartWorkingHeight / 2); + chartWorkingHeight -= ((captionSpace.top || 0) + (captionSpace.bottom || 0)); + if (conf.showLegend) { + chart.hasLegend = true; + if (pluck (fcJSONChart.legendposition, POSITION_BOTTOM).toLowerCase () !== POSITION_RIGHT) { + legendSpace = legend._manageLegendPosition (chartWorkingHeight / 2); + chartWorkingHeight -= legendSpace.bottom; + } else { + legendSpace = legend._manageLegendPosition (chartWorkingHeight / 2); + chartWorkingWidth -= legendSpace.right; + } + } + chart._allocateSpace (legendSpace); + // Now get the max width required for all display text + // set the style + SmartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow); + if (length !== 1) { // Fix for single data in Pie makes pie very small in size. + for (; length--; ) { + SmartLabel.setStyle (data[length].config.style || chartConfig.dataLabelStyle); + textWidthArr[length] = textObj = SmartLabel.getOriSize (data[length].config.displayValue); + labelMaxW = mathMax (labelMaxW, textObj.width); + } + } + // If redius not supplyed then auto calculate it + if (pieRadius === 0) { + pieMinRadius = chart._stubRadius (chartWorkingWidth, labelMaxW, chartWorkingHeight, labelDistance, + slicingDistance, lineHeight, pieMinRadius); + } + else { + conf.slicingDistance = slicingDistance; + conf.pieMinRadius = pieMinRadius; + dataLabelOptions.distance = labelDistance; + } + avaiableMaxpieSliceDepth = chartWorkingHeight - (2 * ((pieMinRadius * pieYScale) + lineHeight)); + conf.managedPieSliceDepth = (pieSliceDepth > avaiableMaxpieSliceDepth) ? (pieSliceDepth - + avaiableMaxpieSliceDepth) : conf.pieSliceDepth; + dataLabelOptions.isSmartLineSlanted = isSmartLineSlanted; + dataLabelOptions.enableSmartLabels = enableSmartLabels; + dataLabelOptions.skipOverlapLabels = skipOverlapLabels; + dataLabelOptions.manageLabelOverflow = manageLabelOverflow; + }, + // manages the spaces when no radius is given. + _stubRadius: function (chartWorkingWidth, labelMaxW, chartWorkingHeight, labelDistance, slicingDistance, + lineHeight, pieMinRadius) { + var chart = this, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + conf = dataSet.config, + fcJSONChart = chart.jsonData.chart, + userGivenSlicingDist = pluckNumber (fcJSONChart.slicingdistance), + dataLabelOptions = conf.dataLabelOptions || + (conf.dataLabelOptions = dataSet._parseDataLabelOptions ()), + availableRadius = 0, + // Slicing distance can not be less then the MINSLICINGDIST (10) + MINSLICINGDIST = 10, + shortFall; + availableRadius = mathMin((chartWorkingWidth / 2) - labelMaxW - slicingDistance, + (chartWorkingHeight / 2) - lineHeight) - labelDistance; + if (availableRadius >= pieMinRadius) {//there has space for min width + pieMinRadius = availableRadius; + } + else if (!userGivenSlicingDist) {/** @todo smartyfy Labels */ + // If slicing distance is not given by the user, adjust slicing distance based on pie radius. + shortFall = pieMinRadius - availableRadius; + // Now reduce the pie slicing distance, but restrict the minimum slicing distance to 10 pixels. + slicingDistance = labelDistance = mathMax(mathMin(labelDistance - shortFall, + slicingDistance), MINSLICINGDIST); + } + conf.slicingDistance = slicingDistance; + conf.pieMinRadius = pieMinRadius; + dataLabelOptions.distance = labelDistance; + + return pieMinRadius; + }, + getDataSet: function (index) { + return this.components.dataset[index]; + }, + _startingAngle: function (angle, relative) { + var ang, + chart = this, + dataSet = chart.components.dataset[0], + seriesData = dataSet.config, + // Angle is differently handled in Pie2D and Pie3D. So, angles is converted + // accordingly to the same base. Its radian in 2D while in degrees in 3D. + // Moreover, sense of positive angle is opposite in the two. + currentAngle = (ang = seriesData.startAngle) * (-rad2deg) + + ((-1) * ang < 0 ? 360 : 0); + if (!isNaN(angle)) { + if (!(seriesData.singletonCase || seriesData.isRotating)) { + angle += relative ? currentAngle : 0; + seriesData.startAngle = -angle * deg2rad; + chart._rotate(angle); + currentAngle = angle; + } + else { + currentAngle = seriesData.startAngle; + } + } + // Angle normalised in the range of [0, 360] + return mathRound(((currentAngle %= 360) + (currentAngle < 0 ? 360 : 0)) * 100) / 100; + }, + eiMethods: /** @lends FusionCharts# */ { + /** + * Pie charts have slices that can be clicked to slice in and out. + * Checks whether a particular wedge of Pie or Doughnut chart is sliced-out or sliced-in. + * + * > Available on `pie` and `doughnut` chart types only. + * + * @group chart:pie-slice + * + * @param {number} index - The index of the data corresponding to the pie/doughnut slice. + * @returns {boolean} - The sliced state of the pie/doughnut wedge. Returns `true` if it's sliced out, + * or `false` if it's sliced in. + * + * @example + * // Render a pie 2d chart with some data in sliced out state, provide data index + * // in an input textfield and get the sliced state of the pie on click of a button + * FusionCharts.ready(function () { + * var chart = new FusionCharts({ + * type: "pie2d", + * renderAt: "chart-container", + * dataSource: "data.json", + * dataFormat: "jsonurl" + * }).render(); + * + * // Get the sliced state of a pie returned when clicked on a button + * // (with an id pie-sliced-state). It picks the data index from + * // an input textfield (with id pie-data-index). + * document.getElementById("pie-sliced-state").onclick = function () { + * var dataIndex = document.getElementById("pie-data-index").value, + * slicedState = chart.isPlotItemSliced(dataIndex); + * }; + * }); + */ + isPlotItemSliced: function (index) { + var num, + data, + config, + dataSet, + apiInstance = this.apiInstance; + + return (apiInstance && apiInstance.components.dataset && + (dataSet = apiInstance.components.dataset[0]) && + (data = dataSet.components.data) && (num = data.length) && + data[index] && (config = data[index].config)) && + config.sliced; + }, + + addData: function () { + var dataSet, + apiInstance = this.apiInstance; + return (apiInstance && apiInstance.components.dataset && + (dataSet = apiInstance.components.dataset[0]) && dataSet.addData.apply(dataSet, arguments)); + }, + + removeData: function () { + var dataSet, + apiInstance = this.apiInstance; + return (apiInstance && apiInstance.components.dataset && + (dataSet = apiInstance.components.dataset[0]) && dataSet.removeData.apply(dataSet, arguments)); + }, + + updateData: function () { + var dataSet, + apiInstance = this.apiInstance; + return (apiInstance && apiInstance.components.dataset && + (dataSet = apiInstance.components.dataset[0]) && dataSet.updateData.apply(dataSet, arguments)); + }, + + /** + * Pie charts have slices. These slices can be clicked by users to slice in or slice out. + * Slices a pie/doughnut wedge to in / out state. In absence of the optional second parameter, it + * toggles the sliced state of the pie. The second parameter only enforces a specific sliced state. + * + * > Available on `pie` and `doughnut` chart types only. + * + * @group chart:pie-slice + * + * @param {number} index - The index of the data corresponding to the pie/doughnut slice. + * @param {boolean=} [slice] - Gives direction to chart on what is the required sliced state. For + * `true`, it slices out, if in sliced-in state. Or else, maintains it's sliced-out state. And + * vice-versa. + * + * @returns {boolean} - The final sliced state of the pie/doughnut wedge. Returns `true` if it's + * sliced out, or `false` if it's sliced in. + * + * @fires FusionCharts#slicingStart + * @fires FusionCharts#slicingEnd + * + * @example + * // Render a pie 2d chart, provide data index in an input textfield + * // and toggle the sliced state of the pie on click of a button + * FusionCharts.ready(function () { + * var chart = new FusionCharts({ + * type: "pie2d", + * renderAt: "chart-container", + * dataSource: "data.json", + * dataFormat: "jsonurl" + * }).render(); + * + * // Toggle the sliced state of the pie when clicked on a button + * // (with an id pie-sliced-state). It picks the data index from + * // an input textfield (with id pie-data-index). + * document.getElementById("pie-sliced-state").onclick = function () { + * var dataIndex = document.getElementById("pie-data-index").value; + * chart.slicePlotItem(dataIndex); + * }; + * }); + */ + slicePlotItem: function (index, slice, callback) { + var dataSet, + data, + config, + num, + output, + sliceVal = !!slice, + apiInstance = this.apiInstance, + asyncRender = apiInstance.chartInstance.args.asyncRender, + list = apiInstance.getJobList(); + + if (callback || asyncRender) { + list.eiMethods.push(schedular.addJob(function(){ + output = (apiInstance && apiInstance.components.dataset && + (dataSet = apiInstance.components.dataset[0]) && (data = dataSet.components.data) && + (num = data.length) && data[index = dataSet.config.reversePlotOrder ? + (num - index - 1) : index] && (config = data[index].config)) && + ((sliceVal !== config.sliced || slice === UNDEFINED) && + apiInstance._plotGraphicClick.call(data[index].graphics.element) || config.sliced); + if (typeof callback === 'function') { + callback(output); + } + }, apiInstance, [], priorityList.postRender)); + } + else { + return (apiInstance && apiInstance.components.dataset && + (dataSet = apiInstance.components.dataset[0]) && + (data = dataSet.components.data) && (num = data.length) && + data[index = dataSet.config.reversePlotOrder ? (num - index - 1) : index] && + (config = data[index].config)) && ((sliceVal !== config.sliced || slice === UNDEFINED) && + apiInstance._plotGraphicClick.call(data[index].graphics.element) || config.sliced); + } + }, + + /** + * Sets the center label in Dougnut 2D chart. The label cosmetics are configurable via the second + * optional parameter, which accepts a host of related properties. + * + * > Available on `doughnut` chart only. + * + * @group chart:pie-center-label + * + * @param {string} labelText - The text to be displayed at doughnut center. + * @param {object=} [options] - The optional parameter that holds a host of configurable params + * with most them being cosmetic properties of the center label. The properties are case sensitive. + * + * @param {string=} [options.font] - Sets the font face of the label. + * @param {string=} [options.fontSize] - Defines the font size of the label. + * @param {boolean=} [options.bold] - Specifies of whether the label be bold. + * @param {boolean=} [options.italic] - Specifies of whether the label be in italic. + * @param {hexcolor=} [options.color] - Sets the color of the label text. + * @param {alpha=} [options.alpha] - Sets the opacity of the label text. + * @param {hexcolor=} [options.hoverColor] - Sets the hover color of the label text. + * @param {alpha=} [options.hoverAlpha] - Sets the hover opacity of the label text. + * @param {hexcolor=} [options.bgColor] - Sets the color of the label background. + * @param {alpha=} [options.bgAlpha] - Sets the opacity of the label background. + * @param {hexcolor=} [options.borderColor] - Sets the color of the label background border. + * @param {alpha=} [options.borderAlpha] - Sets the opacity of the label background border. + * @param {number=} [options.borderThickness] - Sets the thickness of the label background border. + * @param {number=} [options.borderRadius] - Sets the radius for rounded label background. + * @param {number=} [options.padding] - The padding between extremities of the label and inner periphery + * of the doughnut. For rectangular label background, it's relative to any of the 4 corners. While for + * circular background, it's the gap between the 2 concentric circles, background border and inner + * periphery. + * @param {number=} [options.textPadding] - For rectangular label background, it's the gutter between + * the text and the background border. While for circular background, it's the minimum space between + * the background border and the containing circle of the text. + * @param {string=} [options.toolText] - Sets the tooltext for the label. + * + * @fires FusionCharts#centerLabelChanged + * + * @example + * // Render a doughnut 2d chart and set center label with some + * // configuring params on click of a button + * FusionCharts.ready(function () { + * var chart = new FusionCharts({ + * type: "doughnut2d", + * renderAt: "chart-container", + * dataSource: "data.json", + * dataFormat: "jsonurl" + * }).render(); + * + * // Assign the functionality of setting the center label when clicked on + * // a button (with an id set-center-label). + * document.getElementById("set-center-label").onclick = function () { + * chart.centerLabel("The central label", {bold: true, toolText: "center label tooltext"}); + * }; + * }); + */ + centerLabel: function (labelText, options) { + var chart = this.apiInstance, + list = chart.getJobList(), + asyncRender = chart.chartInstance.args.asyncRender, + _helperFn = function() { + var seriesData = chart.components.dataset[0], + config = seriesData.config, + piePlotOptions = config.piePlotOptions, + innerSize = piePlotOptions.innerSize, + pieCenter = config.pieCenter, + cx = pieCenter[0], + cy = pieCenter[1], + centerLabelConfig = config.centerLabelConfig, + key; + + if (typeof options !== 'object') { + options = centerLabelConfig; + } + else { + // Create the config cosmetics object from those obtained + // from argument and default values + for (key in centerLabelConfig) { + options[key] === UNDEFINED && (options[key] = centerLabelConfig[key]); + } + } + options.label = labelText; + seriesData.centerLabelConfig = options; + + innerSize && chart.drawDoughnutCenterLabel(labelText || '', + cx, cy, innerSize, innerSize, options, true); + }; + + if (asyncRender) { + list.eiMethods.push(schedular.addJob(_helperFn, chart, [], priorityList.postRender)); + } + else { + _helperFn(); + } + }, + + /** + * Rotates the pie/doughnut chart to a specific angle or by a specific angle. The mode of + * operation is controlled by the optional second parameter. Even the first parameter is optional, + * in absence of which, the chart doesn't rotate and simply returns the current starting angle + * of the pie/doughnut chart. + * + * Starting angle of a pie/doughnut chart is the angle at which the starting face of the first data is + * aligned to. Each pie is drawn in counter clock-wise direction. + * + * > Available on `pie` and `doughnut` chart types only. + * + * @group chart:pie-slice + * + * @param {degrees=} [angle=0] - The angle by which to rotate the entire pie/doughnut chart. + * @param {boolean=} [relative=false] - Specify whether the angle being set is relative to the current + * angle or with respect to absolute 0. + * @returns {degrees} - The final state of the starting angle of the chart. + * + * @example + * // Render a pie 2d chart and rotate the chart by 90 degrees on click of a button + * FusionCharts.ready(function () { + * var chart = new FusionCharts({ + * type: "pie2d", + * renderAt: "chart-container", + * dataSource: "data.json", + * dataFormat: "jsonurl" + * }).render(); + * + * // Assign the functionality of rotating the chart by 90 degrees when clicked on + * // a button (with an id rotate-chart). + * document.getElementById("rotate-chart").onclick = function () { + * chart.startingAngle(90, true); + * }; + * }); + */ + startingAngle: function (angle, relative, callback) { + var chart = this.apiInstance, + asyncRender = chart.chartInstance.args.asyncRender, + list = chart.getJobList(), + output; + + if (callback || asyncRender) { + list.eiMethods.push(schedular.addJob(function(){ + output = chart._startingAngle(angle, relative); + if (typeof callback === 'function') { + callback(output); + } + }, chart, [], priorityList.postRender)); + } + else { + return chart._startingAngle(angle, relative); + } + } + } + }, chartAPI.guageBase, { + plotborderthickness: 1, + alphaanimation: 0, + singletonPlaceValue: true, + usedataplotcolorforlabels: 0, + enableslicing: ONESTRING + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-pie3d', function () { + var global = this, + lib = global.hcLib, + R = lib.Raphael, + hasTouch = lib.hasTouch, + win = global.window, + getPosition = lib.getPosition, + //add the tools thats are requared + pluck = lib.pluck, + pluckNumber = lib.pluckNumber, + ROLLOVER = 'DataPlotRollOver', + ROLLOUT = 'DataPlotRollOut', + EVENTARGS = 'eventArgs', + each = lib.each, + plotEventHandler = lib.plotEventHandler, + + docMode8 = win.document.documentMode === 8, + HIDDEN = 'hidden', + VISIBLE = docMode8 ? 'visible' : '', + M = 'M', + L = 'L', + V = 'v', + A = 'A', + Z = 'Z', + math = Math, + mathSin = math.sin, + mathCos = math.cos, + mathRound = math.round, + mathATan2 = math.atan2, + mathMin = math.min, + mathMax = math.max, + mathAbs = math.abs, + mathPI = math.PI, + mathCeil = math.ceil, + mathFloor = math.floor, + pi = Math.PI, + piBy2 = pi / 2, + pi2 = 2 * pi, + pi3By2 = pi + piBy2, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + getClickArcTangent = function (x, y, center, ref, pieYScale) { + return mathATan2 ((y - center[1] - ref.top) / pieYScale, x - center[0] - ref.left); + }, + getAbsScaleAngle = function (start, end) { + return (start > end ? pi2 : 0) + end - start; + }, + getDarkColor = lib.graphics.getDarkColor, + getLightColor = lib.graphics.getLightColor, + getFirstValue = lib.getFirstValue, + convertColor = lib.graphics.convertColor, + COMMASTRING = lib.COMMASTRING, + toRaphaelColor = lib.toRaphaelColor, + hasSVG = lib.hasSVG, + UNDEFINED, + map = function (arr, fn) { + var results = [], + i = 0, + len = arr.length; + + for (; i < len; i++) { + results[i] = fn.call (arr[i], arr[i], i, arr); + } + return results; + }, + + /* Helper function */ + normalizeAngle = function (angle, inDegrees) { + var fullCycle = inDegrees ? 360 : pi2; + angle = (angle || 0) % fullCycle; + return angle < 0 ? fullCycle + angle : angle; + }, + defined = function (obj) { + return obj !== UNDEFINED && obj !== null; + }, + isObject = function (obj) { + return typeof obj === 'object'; + }, + isString = function (s) { + return typeof s === 'string'; + }, + pInt = function (s, mag) { + return parseInt (s, mag || 10); + }, + pie3DCacheColorStore = { + lighting3D: { + + }, + lighting2D: { + + } + }; + + chartAPI ('pie3d', { + defaultDatasetType : 'Pie3D', + applicableDSList: { + 'Pie3D': true + }, + is3D: true, + friendlyName: '3D Pie Chart', + fireGroupEvent: true, + creditLabel: creditLabel, + getPointColor: function (color) { + return color; + }, + _configureManager: function () { + var chart = this, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + dataSetConfig = dataSet.config, + dataSetComponents = dataSet.components, + pie3DManager = dataSetComponents.Pie3DManager, + data = dataSetComponents.data; + if (pie3DManager) { + pie3DManager.configure(dataSetConfig.pieSliceDepth, data.length === 1, dataSetConfig.use3DLighting, + false); + } + }, + // Pie2D (base) has defaultPlotShadow, but 3d does not. + defaultPlotShadow: 0, + //Initialising the pie3D manager class. + _preDrawCalculate: function () { + var i, + point, + chart = this, + chartConfig = chart.config, + chartComponents = chart.components, + precision = 1000, + total = 0, + dataSet = chartComponents.dataset[0], + dataSetConfig = dataSet.config, + dataSetComponents = dataSet.components, + dataLabelOptions = dataSetConfig.dataLabelOptions, + pie3DOptions = (dataSetConfig.pie3DOptions = dataSet._parsePie3DOptions()), + startingAngle = (pluck (dataSetConfig.startAngle, 0) % pi2), + fontSize, + managedPieSliceDepth = dataSetConfig.managedPieSliceDepth, + slicedOffset = dataSetConfig.slicedOffset = pie3DOptions.slicedOffset, + plotWidth = chartConfig.canvasWidth, + plotHeight = chartConfig.canvasHeight, + positions = [chartConfig.canvasLeft + plotWidth * 0.5, + chartConfig.canvasTop + plotHeight * 0.5 - managedPieSliceDepth * 0.5], + start, + pointConfig, + end, + angle, + lastEnd, + maxEnd, + data = dataSetComponents.data, + fraction, + smallestSize = mathMin (plotWidth, plotHeight), + isPercent, + radiusX, // the x component of the radius vector for a given point + radiusY, + labelDistance = dataLabelOptions.distance, + pieYScale = dataSetConfig.pieYScale, + + slicedOffsetY = dataSetConfig.slicedOffsetY || (dataSetConfig.slicedOffsetY = + slicedOffset * dataSetConfig.pieYScale), + pie3DManager = dataSetComponents.Pie3DManager; + + // get positions - either an integer or a percentage string must be given + positions.push ((2 * dataSetConfig.pieMinRadius), pie3DOptions.innerSize || 0); + + positions = map (positions, function (length, i) { + isPercent = /%$/.test (length); + return isPercent ? + // i == 0: centerX, relative to width + // i == 1: centerY, relative to height + // i == 2: size, relative to smallestSize + // i == 4: innerSize, relative to smallestSize + [plotWidth, plotHeight - managedPieSliceDepth, smallestSize, smallestSize][i] * + pInt (length) / 100: + length; + }); + + + //convert all diameter into radius + positions[2] /= 2; + positions[3] /= 2; + //Add the ry + positions.push (positions[2] * pieYScale); + //centerRadiusX + positions.push ((positions[2] + positions[3]) / 2); + //centerRadiusY + positions.push (positions[5] * pieYScale); + + // utility for getting the x value from a given y, used for anticollision logic in data labels + dataSet.getX = function (y, left) { + + angle = math.asin ((y - positions[1]) / (positions[2] + labelDistance)); + + return positions[0] + + (left ? -1 : 1) * + (mathCos (angle) * (positions[2] + labelDistance)); + }; + + // set center for later use + dataSetConfig.center = positions; + + // get the total sum + each (data, function (point) { + total += point.config.y; + }); + + dataSetConfig.labelsRadius = positions[2] + labelDistance; + dataSetConfig.labelsRadiusY = dataSetConfig.labelsRadius * pieYScale; + dataSetConfig.quadrantHeight = (plotHeight - managedPieSliceDepth) / 2; + dataSetConfig.quadrantWidth = plotWidth / 2; + + lastEnd = startingAngle; + + lastEnd = mathRound (lastEnd * precision) / precision; + maxEnd = lastEnd + pi2; + + fontSize = pluckNumber (parseInt (dataLabelOptions.style.fontSize, 10), 10) + 4,//2px padding + dataSetConfig.maxLabels = mathFloor (dataSetConfig.quadrantHeight / fontSize); //max labels per quarter + dataSetConfig.labelFontSize = fontSize; + dataSetConfig.connectorPadding = pluckNumber (dataLabelOptions.connectorPadding, 5); + dataSetConfig.isSmartLineSlanted = pluck (dataLabelOptions.isSmartLineSlanted, true); + dataSetConfig.connectorWidth = pluckNumber (dataLabelOptions.connectorWidth, 1); + dataSetConfig.enableSmartLabels = dataLabelOptions.enableSmartLabels; + if (!pie3DManager) { + pie3DManager = dataSetComponents.Pie3DManager = new Pie3DManager (chart); + // enabling the tooltip options for slices as it is the parent group being used by the pie3dmanager + chart.get('graphics','datasetGroup').trackTooltip(true); + } + chart._configureManager(); + + // each (data, function (point) { + for (i = data.length - 1; i >= 0; i -= 1) { + point = data[i]; + pointConfig = point.config; + // set start and end angle + start = lastEnd; + fraction = total ? pointConfig.y / total : 0; + + lastEnd = mathRound ((lastEnd + (fraction * pi2)) * precision) / precision; + if (lastEnd > maxEnd) { + lastEnd = maxEnd; + } + end = lastEnd; + + // set the shape + pointConfig.shapeArgs = { + start: mathRound (start * precision) / precision, + end: mathRound (end * precision) / precision + }; + + // center for the sliced out slice + pointConfig.centerAngle = angle = ((end + start) / 2) % pi2; + /** @todo: slicedTranslation is implemented as string */ + pointConfig.slicedTranslation = [ + mathRound (mathCos (angle) * slicedOffset), + mathRound (mathSin (angle) * slicedOffsetY) + ]; + + // set the anchor point for tooltips + radiusX = mathCos (angle) * positions[2]; + dataSetConfig.radiusY = radiusY = mathSin (angle) * positions[4]; + pointConfig.tooltipPos = [ + positions[0] + radiusX * 0.7, + positions[1] + radiusY//changed to reducr mouce on tooltip condition + ]; + + // API properties + pointConfig.percentage = fraction * 100; + pointConfig.total = total; + } + }, + placeDataLabels: (function () { + /* + * Pie Helper Functions. + */ + var sortArrayByPoint = function (a, b) { + return a.point.value - b.point.value; + }, + sortArrayByAngle = function (a, b) { + return a.angle - b.angle; + }, + alignments = ['start', 'start', 'end', 'end'], + alignCenter = 'middle', + ySign = [-1, 1, 1, -1], + xSign = [1, 1, -1, -1]; + + return function (isRotating) { + var attr, + isNewElem, + prevTextPos, + chart = this, + chartConfig = chart.config, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + plotOptions = dataSet.config, + plotItems = dataSet.components.data, + piePlotOptions = plotOptions.piePlotOptions, + canvasLeft = chartConfig.canvasLeft, + canvasTop = chartConfig.canvasTop, + canvasWidth = chartConfig.canvasWidth, + cx = canvasLeft + chartConfig.canvasWidth * 0.5, + cy = canvasTop + chartConfig.canvasHeight * 0.5, + smartLabel = chart.linkedItems.smartLabel, + dataLabelsOptions = plotOptions.dataLabelOptions, + style = dataLabelsOptions.style, + lineHeight = pluckNumber (mathCeil (parseFloat (style.lineHeight)), 12), + placeInside = getFirstValue (dataLabelsOptions.placeInside, false), + skipOverlapLabels = dataLabelsOptions.skipOverlapLabels, + manageLabelOverflow = dataLabelsOptions.manageLabelOverflow, + connectorPadding = dataLabelsOptions.connectorPadding, + distanceOption = dataLabelsOptions.distance, + connectorWidth = dataLabelsOptions.connectorWidth, + remainingHeight, + // divide the points into quarters for anti collision + quarters = [ + [], // top right + [], // bottom right + [], // bottom left + [] // top left + ], + // todo remove the dupliace variables. + plotLeft = canvasLeft, + plotTop = canvasTop, + plotWidth = canvasWidth, + labelFontSize = parseInt (style.fontSize, 10), + labelHeight = labelFontSize, + halfLabelHeight = labelHeight / 2, + xDisplacement = [connectorPadding, + connectorPadding, -connectorPadding, -connectorPadding + ], + isSmartLineSlanted = dataLabelsOptions.isSmartLineSlanted, + align, + i, + labelWidth, + j, + oriY, + maxYmayHave, + spaceRequired, + length, + k, + sliced, + x1, + x2, + x3, + x4, + y1, + y2, + y3, + points, + point, + angle, + excess, + excessArr, + dataLabel, + quarter, + transX, + transY, + smartLabelObj, + pointConfig, + pointGraphics, + connector, + connectorPath, + outside = distanceOption > 0, + center = plotOptions.center || (plotOptions.center = [ + cx, + cy, + piePlotOptions.size, + (piePlotOptions.innerSize || 0) + ]), + centerY = center[1], + centerX = center[0], + radius = center[2], + radiusY = center[4], + dataLabelsRadius = plotOptions.labelsRadius, + dataLabelsRadiusY = mathRound (plotOptions.labelsRadiusY * 100) / 100, + maxLabels = plotOptions.maxLabels, + enableSmartLabels = plotOptions.enableSmartLabels, + labelQuardentHeight, + maxQuardentLabel, + pieSliceDepthHalf = plotOptions.pieSliceDepth / 2, + animationObj = chart.get('config', 'animationObj'), + animationDuration = isRotating ? 0 : animationObj.duration, + mainElm = animationObj.dummyObj, + animObj = animationObj.animObj, + animType = animationObj.animType, + _textCss, + _textAttrs, + plotDragMove = chart._plotDragMove, + plotDragStart = chart._plotDragStart, + plotDragEnd = chart._plotDragEnd, + plotRollOver = chart._plotRollOver, + plotRollOut = chart._plotRollOut, + paper = chartComponents.paper, + datasetGraphics = dataSet.graphics, + dataLabelContainer = datasetGraphics.dataLabelContainer; + smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow); + // save the world if there is no labels to be placed. + if (!plotOptions.dataLabelCounter) { + return; + } + if (!isRotating) { + //do not set the style every time + // Do it for first time + smartLabel.setStyle (style); + } + + // arrange points for detection collision + // Creates an array of quarter containing labels of each quarter + //if there has only one label the draw it inside + if (plotItems.length == 1) { + + point = plotItems[0]; + pointGraphics = point.graphics; + pointConfig = point.config; + _textAttrs = pointConfig._textAttrs; + _textCss = pointConfig._textCss; + dataLabel = pointGraphics.label; + connector = pointGraphics.connector; + pointConfig.slicedTranslation = [plotLeft, plotTop]; + if (pointConfig.y !== null && pointConfig.y !== undefined) { + _textAttrs.visibility = VISIBLE; + _textAttrs['text-anchor'] = alignCenter; + _textAttrs.x = centerX; + _textAttrs.y = centerY + halfLabelHeight - 2; + + _textAttrs._x = centerX; + } + if (!dataLabel) { + dataLabel = pointGraphics.label = paper + .text(_textAttrs, _textCss, dataLabelContainer) + .drag(plotDragMove, plotDragStart, plotDragEnd) + .hover(plotRollOver, plotRollOut); + } + else { + dataLabel.animateWith (mainElm, animObj, _textAttrs, animationDuration, animType); + } + if (_textAttrs._x) { + dataLabel.x = _textAttrs._x; + delete _textAttrs.x; + } + dataLabel + .data('plotItem', _textAttrs.plotItem) + .data(EVENTARGS, _textAttrs.eventArgs); + + if (_textAttrs.visibility === VISIBLE) { + dataLabel.show(); + } + + if (connector) { + connector.hide(); + } + } + else { + if (placeInside) { + each (plotItems, function (point) { + pointGraphics = point.graphics; + pointConfig = point.config; + _textAttrs = pointConfig._textAttrs; + dataLabel = pointGraphics.label; + if (pointConfig.y !== null && pointConfig.y !== undefined) { + angle = pointConfig.angle; + y3 = centerY + (center[6] * mathSin (angle)) + halfLabelHeight - 2; + x3 = centerX + (center[5] * mathCos (angle)); + _textAttrs._x = x3; + _textAttrs._y = y3; + if (pointConfig.sliced) { + var slicedTranslation = point.slicedTranslation, + transX = slicedTranslation[0] - plotLeft, + transY = slicedTranslation[1] - plotTop; + x3 = x3 + transX; + y3 = y3 + transY; + } + _textAttrs.visibility = VISIBLE; + _textAttrs.align = alignCenter; + _textAttrs.x = x3; + _textAttrs.y = y3; + } + + if (!dataLabel) { + dataLabel = pointGraphics.label = paper + .text(_textAttrs, _textCss, dataLabelContainer) + .drag(plotDragMove, plotDragStart, plotDragEnd) + .hover(plotRollOver, plotRollOut); + } + else { + dataLabel.animateWith (mainElm, animObj, _textAttrs, animationDuration, animType); + } + + dataLabel + .data('plotItem', _textAttrs.plotItem) + .data(EVENTARGS, _textAttrs.eventArgs); + + if (_textAttrs.visibility === VISIBLE) { + dataLabel.show(); + } + + dataLabel.x = _textAttrs._x; + dataLabel._x = _textAttrs._x; + dataLabel._y = _textAttrs._y; + }); + } + else { //outside + each (plotItems, function (point) { + pointGraphics = point.graphics; + pointConfig = point.config; + _textCss = pointConfig._textCss; + _textAttrs = pointConfig._textAttrs; + if (!(_textAttrs.text = pointConfig.displayValue)) { + return; + } + pointGraphics = point.graphics; + if (pointConfig.y !== null && pointConfig.y !== undefined) { + dataLabel = pointGraphics.label; + connector = pointGraphics.connector; + connector && connector.show(); + dataLabel && dataLabel.show (); + } + + dataLabel = pointGraphics.label; + + angle = pointConfig.angle; + + if (angle < 0) { + angle = pi2 + angle; + } + // Calculate top right quarter labels + if (angle >= 0 && angle < piBy2) { + quarter = 1; + } else + // Calculate bottom right quarter labels + if (angle < pi) { + quarter = 2; + } else + // Calculate bottom left quarter labels + if (angle < (pi3By2)) { + quarter = 3; + } + // Calculate bottom left quarter labels + else { + quarter = 0; + } + // Now put labels according to each quarter + quarters[quarter].push ( { + point : point, + angle : angle + }); + + /*dataLabel = pointGraphics.label; + if (dataLabel) { + angle = pointConfig.angle; + + if (angle < 0) { + angle = pi2 + angle; + } + // Calculate top right quarter labels + if (angle >= 0 && angle < piBy2) { + quarter = 1; + } else + // Calculate bottom right quarter labels + if (angle < pi) { + quarter = 2; + } else + // Calculate bottom left quarter labels + if (angle < (pi3By2)) { + quarter = 3; + } + // Calculate bottom left quarter labels + else { + quarter = 0; + } + // Now put labels according to each quarter + quarters[quarter].push ( { + point : point, + angle : angle + }); + }*/ + }); + + i = k = 4; + //if excess then remove the low value slice first + while (i --) { + if (skipOverlapLabels) { + // Find labels can fit into the quarters or not + excess = quarters[i].length - maxLabels; + if (excess > 0) { + quarters[i].sort (sortArrayByPoint); // sort by point.y + // remove extra data form the array + // which labels can not be fitted into the quarters + excessArr = quarters[i].splice (0, excess); + //hide all removed labels + for (j = 0, length = excessArr.length; j < length; j += 1) { + point = excessArr[j].point; + pointGraphics = point.graphics; + if (pointGraphics.label) { + pointGraphics.label.attr('visibility', HIDDEN); + } + if (pointGraphics.connector) { + pointGraphics.connector.attr ( { + visibility: HIDDEN + }); + } + } + } + } + // now we sort the data labels by its label angle + quarters[i].sort (sortArrayByAngle); + } + + maxQuardentLabel = mathMax ( + quarters[0].length, + quarters[1].length, + quarters[2].length, + quarters[3].length + ); + labelQuardentHeight = mathMax ( + mathMin (maxQuardentLabel, maxLabels) * labelHeight, + dataLabelsRadiusY + labelHeight + ); + + // reverse 1st and 3rd quardent points + quarters[1].reverse (); + quarters[3].reverse (); + smartLabel.setStyle (style); + + while (k --) { + points = quarters[k]; + length = points.length; + + if (!skipOverlapLabels) { + if (length > maxLabels) { + labelHeight = labelQuardentHeight / length; + } + else { + labelHeight = labelFontSize; + } + halfLabelHeight = labelHeight / 2; + } + + //1st pass + //place all labels at 1st quarter + + // calculate the total available space to put labels + spaceRequired = length * labelHeight; + // calculate the remaining height + remainingHeight = labelQuardentHeight; + //place all child point + for (i = 0; i < length; i += 1, spaceRequired -= labelHeight) { + // Get the y position of the label (radius where data label is to draw) + oriY = mathAbs (labelQuardentHeight * mathSin (points[i].angle)); + if (remainingHeight < oriY) { + oriY = remainingHeight; + } + else if (oriY < spaceRequired) { + oriY = spaceRequired; + } + remainingHeight = (points[i].oriY = oriY) - labelHeight; + } + + //2nd pass (reverse) + align = alignments[k]; + //place all labels at 1st quarter + maxYmayHave = labelQuardentHeight - ((length - 1) * labelHeight); + remainingHeight = 0; + + //place all child point + for (i = points.length - 1; i >= 0; i -= 1, maxYmayHave += labelHeight) { + point = points[i].point; + pointGraphics = point.graphics; + pointConfig = point.config; + _textAttrs = pointConfig._textAttrs; + _textCss = pointConfig._textCss; + + if (pointConfig.y === null || !_textAttrs.text) { + continue; + } + angle = points[i].angle; + sliced = pointConfig.sliced; + dataLabel = pointGraphics.label; + + oriY = mathAbs (labelQuardentHeight * mathSin (angle)); + + if (oriY < remainingHeight) { + oriY = remainingHeight; + } + else if (oriY > maxYmayHave) { + oriY = maxYmayHave; + } + + remainingHeight = oriY + labelHeight; + + y1 = ((oriY + points[i].oriY) / 2); + x1 = centerX + xSign[k] * dataLabelsRadius * mathCos (math.asin (y1 / + labelQuardentHeight)); + + y1 *= ySign[k]; + y1 += centerY; + + y2 = centerY + (radiusY * mathSin (angle)); + x2 = centerX + (radius * mathCos (angle)); + + // Relation: centerX <= connectorStartX <= connectorEndX (for right half and vice + // versa for left half) + (k < 2 && x1 < x2 || k > 1 && x1 > x2) && (x1 = x2); + + x3 = x1 + xDisplacement[k]; + y3 = y1 + halfLabelHeight - 2; + x4 = x3 + xDisplacement[k]; + + _textAttrs._x = x4; + + if (manageLabelOverflow) { + labelWidth = k > 1 ? x4 - canvasLeft: canvasLeft + plotWidth - x4; + smartLabel.setStyle (pointConfig.style); + lineHeight = pluckNumber (mathCeil (parseFloat (pointConfig.style.lineHeight)), + 12) + ((mathCeil (parseFloat (pointConfig.style.border), 12) * 2) || 0); + smartLabelObj = smartLabel.getSmartText (pointConfig.displayValue, labelWidth, + lineHeight); + _textAttrs.text = smartLabelObj.text; + _textAttrs.tooltip = smartLabelObj.tooltext; + } + + //shift the labels at front pieSliceDepthHalf + if (angle < pi) { + y1 += pieSliceDepthHalf; + y2 += pieSliceDepthHalf; + y3 += pieSliceDepthHalf; + } + _textAttrs._y = y3; + // dataLabel.y = y3; + if (sliced) { + + transX = pointConfig.transX; + transY = pointConfig.transY; + x3 = x3 + transX; + x1 = x1 + transX; + x2 = x2 + transX; + y2 = y2 + transY; + x4 = x4 + transX; + } + _textAttrs.visibility = VISIBLE; + _textAttrs['text-anchor'] = align; + prevTextPos = dataLabel && dataLabel.data('textPos'); + if (prevTextPos) { + dataLabel.attr({ + x: prevTextPos.x, + y: prevTextPos.y + }); + } + _textAttrs.x = x4; + _textAttrs.y = y1; + if (!isRotating && prevTextPos) { + dataLabel.animateWith(mainElm, animObj, _textAttrs, animationDuration, + animType); + } + else { + if (!dataLabel) { + dataLabel = pointGraphics.label = paper + .text(_textAttrs, _textCss, dataLabelContainer) + .drag(plotDragMove, plotDragStart, plotDragEnd) + .hover(plotRollOver, plotRollOut); + } + else { + _textAttrs && dataLabel.attr(_textAttrs); + } + } + dataLabel.data('textPos', { + x: x4, + y: y1 + }) + .data('plotItem', _textAttrs.plotItem) + .data(EVENTARGS, _textAttrs.eventArgs); + + dataLabel.x = _textAttrs._x; + // storing original x value + // to use while slicing in (IE Issue original x get changed form animate) + dataLabel._x = _textAttrs._x; + dataLabel.y = _textAttrs._y; + + if (_textAttrs.tooltip) { + dataLabel.tooltip(_textAttrs.tooltip); + delete _textAttrs.tooltip; + } + //draw the connector + // draw the connector + if (outside && connectorWidth && enableSmartLabels) { + connector = pointGraphics.connector; + if (!pointConfig.connectorPath) { + isNewElem = true; + } + pointConfig.connectorPath = connectorPath = [ + M, + x2, y2, // base + L, + isSmartLineSlanted ? x1 : x2, y1, // first break, next to the label + x3, y1 // end of the string at the label + ]; + + attr = { + path: connectorPath, + 'stroke-width': connectorWidth, + stroke: dataLabelsOptions.connectorColor || '#606060', + visibility: VISIBLE + }; + if (connector) { + if (!isRotating && !isNewElem) { + connector.animateWith(mainElm, animObj, attr, animationDuration, + animType); + } + else { + connector.attr(attr); + + } + } + } + } + } + } + } + }; + } ()), + animate: function () { + var i, + point, + graphic, + pointGraphics, + pointConfig, + element, + args, + up, + start, + end, + chart = this, + chartGraphics = chart.graphics, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + dataSetConfig = dataSet.config, + dataSetComponents = dataSet.components, + plotItems = dataSetComponents.data, + dataSetGroup = chartGraphics.datasetGroup, + len = plotItems.length, + alphaAnim = dataSetConfig.alphaAnimation, + animStartFN = function () { + if (chart.disposed || chart.disposing) { + return; + } + chart.placeDataLabels (false); + }, + animationObj = chart.get('config', 'animationObj'), + animationDuration = animationObj.duration || 0, + mainElm = animationObj.dummyObj, + animObj = animationObj.animObj, + animType = animationObj.animType; + + if (alphaAnim) { + dataSetGroup.attr ( { + opacity: 0 + }); + dataSetGroup.animateWith(mainElm, animObj, { + opacity: 1 + }, animationDuration, animType, animStartFN); + } + else { + for (i = 0; i < len; i++) { + point = plotItems[i]; + pointGraphics = point.graphics; + pointConfig = point.config; + args = pointConfig.shapeArgs; + up = 2 * mathPI; + element = pointGraphics.element; + + // start values + if (element) { + element.attr ( { + start: up, + end: up + }); + + start = args.start; + end = args.end; + + /* Raphael animation do not support start and end attributes. + * Since the attribute setting for Pie3D goes through attrFN + * method of Pie3DManager, we can safely use some unused + * attributes for pie3D to pass through Raphael animation module + * and trap the attributes to convert to start and end in attrFN */ + graphic.animateWith (mainElm, animObj, { + cx: start - up, + cy: end - up + }, animationDuration, animType); + } + } + } + }, + _rotate: function (setAngle) { + var chart = this, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + dataSetConfig = dataSet.config, + dataSetComponents = dataSet.components, + data = dataSetComponents.data, + slicedOffset = dataSetConfig.slicedOffset, + slicedOffsetY = dataSetConfig.slicedOffsetY, + startingAngle = dataSetConfig.startAngle, + angle; + + setAngle = !isNaN (setAngle) ? setAngle : -dataSetConfig._lastAngle; + + angle = (setAngle - startingAngle) % 360; + + dataSetConfig.startAngle = pluckNumber (setAngle, + dataSetConfig.startAngle) % 360; + + angle = - (angle * mathPI) / 180; + + if (dataSetComponents.Pie3DManager) { + dataSetComponents.Pie3DManager.rotate (angle); + } + + each (data, function (point) { + var slicedTranslation = [], + pointGraphics = point.graphics, + pointConfig = point.config, + element = pointGraphics.element, + args = pointConfig.shapeArgs, + newAngleArgs = { + start: (args.start += angle), + end: (args.end += angle) + }, + pointAngle = pointConfig.angle = normalizeAngle ((newAngleArgs.start + newAngleArgs.end) / 2), + sliced = pointConfig.sliced, + cosAngle = mathCos (pointAngle), + sinAngle = mathSin (pointAngle); + + //set the slicedTranslation + slicedTranslation = pointConfig.slicedTranslation = [ + mathRound (cosAngle * slicedOffset), + mathRound (sinAngle * slicedOffsetY) + ]; + + pointConfig.transX = slicedTranslation[0]; + pointConfig.transY = slicedTranslation[1]; + pointConfig.slicedX = sliced ? mathCos (angle) * slicedOffset : 0; + pointConfig.slicedY = sliced ? mathSin (angle) * slicedOffsetY : 0; + + if (element && sliced) { + element.attr ( { + transform: 't' + slicedTranslation[0] + ',' + slicedTranslation[1] + }); + + } + + }); + + chart.placeDataLabels (true, data); + + }, + _plotRollOver: function (e) { + var plotItem = this.data ('plotItem'), + index = plotItem.index, + chart = plotItem.chart, + chartConfig = chart.config, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + dataSetConfig = dataSet.config, + dataSetComponents = dataSet.components, + data = dataSetComponents.data[index], + setGraphics = data.graphics, + setConfig = data.config, + element = setGraphics.element, + hoverEffects = setConfig.hoverEffects; + if (!dataSetConfig.isRotating) { + plotEventHandler.call (element, chart, e, ROLLOVER); + hoverEffects.enabled && element.attr(hoverEffects); + } + chartConfig.isHovered = true; + }, + + _plotRollOut: function (e) { + var plotItem = this.data ('plotItem'), + index = plotItem.index, + chart = plotItem.chart, + chartConfig = chart.config, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + dataSetConfig = dataSet.config, + dataSetComponents = dataSet.components, + data = dataSetComponents.data[index], + setGraphics = data.graphics, + setConfig = data.config, + element = setGraphics.element; + + if (!dataSetConfig.isRotating) { + plotEventHandler.call (element, chart, e, ROLLOUT); + element.attr({ + color: setConfig.color.color.split(',')[0], + alpha: setConfig._3dAlpha, + borderWidth: setConfig.borderWidth, + borderColor: setConfig.borderColor + }); + } + chartConfig.isHovered = false; + }, + + _plotDragStart: function (x, y, evt) { + var plotItem = this.data ('plotItem'), + chart = plotItem.chart, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + dataSetConfig = dataSet.config, + angle; + + dataSetConfig.isRightClicked = !hasTouch && (evt.button !== 0 && evt.button !== 1) ? true : false; + if (!dataSetConfig.enableRotation || dataSetConfig.isRightClicked) { + return; + } + + dataSetConfig.isRotating = false; + angle = getClickArcTangent.call (evt, x, y, dataSetConfig.center, + (dataSetConfig.chartPosition = getPosition(chart.linkedItems.container)), dataSetConfig.pieYScale); + dataSetConfig.dragStartAngle = angle; + dataSetConfig._lastAngle = -dataSetConfig.startAngle; + dataSetConfig.startingAngleOnDragStart = dataSetConfig.startAngle; + }, + + _plotDragEnd: function (e) { + var plotItem = this.data ('plotItem'), + index = plotItem.index, + chart = plotItem.chart, + chartConfig = chart.config, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + dataSetConfig = dataSet.config, + dataSetComponents = dataSet.components, + Pie3DManager = dataSetComponents.Pie3DManager, + startingAng = dataSetConfig.startAngle; + // save state + /*reflowUpdate = { + hcJSON: { + series: [ { + startAngle: startingAng + }] + } + };*/ + /*if (!chart.disposed) { + extend2 (chart.logic.chartInstance.jsVars._reflowData, + reflowUpdate, true); + }*/ + + if (dataSetConfig.isRightClicked) { + return; + } + if (dataSetConfig.isRotating) { + /* The events mouseup, dragend and click are raised in order. In order + * to update the flag isRotating to false post click event, setTimeout + * called, to take immediate effect, is programmed to update the flag. + * Thus, the flag gets updated post the series of events, in effect. + * NB: Click event is subscribed conditionally. + */ + setTimeout (function () { + dataSetConfig.isRotating = false; + }, 0); + /** + * @event FusionCharts#rotationEnd + * @group chart:pie-slice + * + * @param { number} startingAngle - The initial angle. (desc) + * @param { number} changeInAngle - It is the difference between the starting angle and the starting + * angle on the drag start. + */ + global.raiseEvent ('rotationEnd', { + startingAngle: normalizeAngle (startingAng, true), + changeInAngle: startingAng - dataSetConfig.startingAngleOnDragStart + }, chart.chartInstance); + + !chartConfig.isHovered && Pie3DManager.colorObjs[index] && + Pie3DManager.onPlotHover (index, false); + } + else { + chart._plotGraphicClick.call (this,e); + } + }, + + _plotDragMove: function (dx, dy, x, y, evt) { + var plotItem = this.data ('plotItem'), + chart = plotItem.chart, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + dataSetConfig = dataSet.config, + angle, + currentTime, + deltaAngle; + + if (isNaN(dx) || isNaN(dy) || !dataSetConfig.enableRotation || dataSetConfig.singletonCase || + dataSetConfig.isRightClicked) { + return; + } + + angle = getClickArcTangent.call (evt, x, y, dataSetConfig.center, + dataSetConfig.chartPosition, dataSetConfig.pieYScale); + + if (!dataSetConfig.isRotating) { + dataSetConfig.dragStartAngle !== angle && (dataSetConfig.isRotating = true); + /** + * This event is fired when a pie or doughnut chart's rotation is triggered. + * + * @event FusionCharts#rotationStart + * @group chart:pie-slice + * @param { number} startingAngle - This indicates the angle from where rotation started. + */ + global.raiseEvent ('rotationStart', { startingAngle: normalizeAngle (dataSetConfig.startAngle, + true)}, chart.chartInstance); + } + + deltaAngle = angle - dataSetConfig.dragStartAngle; + + dataSetConfig.dragStartAngle = angle; + dataSetConfig.moveDuration = 0; + + dataSetConfig._lastAngle += (deltaAngle * 180 / mathPI); + currentTime = new Date ().getTime (); + + if (!dataSetConfig._lastTime || (dataSetConfig._lastTime + dataSetConfig.timerThreshold < + currentTime)) { + if (!dataSetConfig._lastTime) { + chart._rotate (); + } + dataSetConfig.timerId = setTimeout (function () { + if (!chart.disposed || !chart.disposing) { + chart._rotate (); + } + }, dataSetConfig.timerThreshold); + dataSetConfig._lastTime = currentTime; + } + }, + // manages the spaces when no radius is given. + _stubRadius: function (chartWorkingWidth, labelMaxW, chartWorkingHeight, labelDistance, slicingDistance, + lineHeight, pieMinRadius) { + var chart = this, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + conf = dataSet.config, + fcJSONChart = dataSet.config, + userGivenSlicingDist = pluckNumber (fcJSONChart.slicingdistance), + dataLabelOptions = conf.dataLabelOptions || + (conf.dataLabelOptions = dataSet._parseDataLabelOptions ()), + availableRadius = 0, + // Slicing distance can not be less then the MINSLICINGDIST (10) + MINSLICINGDIST = 10, + pieYScale = conf.pieYScale, + pieSliceDepth = conf.pieSliceDepth, + shortFall; + chartWorkingHeight -= pieSliceDepth; + availableRadius = mathMin ((chartWorkingWidth / 2) - labelMaxW - slicingDistance, + ((chartWorkingHeight / 2) - lineHeight) / pieYScale) - labelDistance; + + if (availableRadius >= pieMinRadius) { //there has space for min width + pieMinRadius = availableRadius; + } + else if (!userGivenSlicingDist) { /** @todo smartyfy Labels */ + // If slicing distance is not given by the user, adjust slicing distance based on pie radius. + shortFall = pieMinRadius - availableRadius; + // Now reduce the pie slicing distance, but restrict the minimum slicing distance to 10 pixels. + slicingDistance = labelDistance = mathMax (mathMin (labelDistance - shortFall, + slicingDistance), MINSLICINGDIST); + } + conf.slicingDistance = slicingDistance; + conf.pieMinRadius = pieMinRadius; + dataLabelOptions.distance = labelDistance; + + return pieMinRadius; + }, + _startingAngle: function (angle, relative) { + var ang, + chart = this, + dataSet = chart.components.dataset[0], + seriesData = dataSet.config, + // Angle is differently handled in Pie2D and Pie3D. So, angles is converted + // accordingly to the same base. Its radian in 2D while in degrees in 3D. + // Moreover, sense of positive angle is opposite in the two. + currentAngle = (ang = seriesData.startAngle) + (ang < 0 ? 360 : 0); + + if (!isNaN(angle)) { + if (!(seriesData.singletonCase || seriesData.isRotating)) { + angle += relative ? currentAngle : 0; + chart._rotate(angle); + currentAngle = angle; + } + } + // Angle normalised in the range of [0, 360] + return mathRound(((currentAngle %= 360) + (currentAngle < 0 ? 360 : 0)) * 100) / 100; + } + }, chartAPI.pie2d, { + plotborderthickness: 0.1, + alphaanimation: 1 + }); + + // **** pie3d manager ***** // + // set the new aimatable properites + if(R._availableAnimAttrs && R._availableAnimAttrs.cx){ + // assuming that cx is also numaric type + R._availableAnimAttrs.innerR = R._availableAnimAttrs.depth = R._availableAnimAttrs.radiusYFactor = + R._availableAnimAttrs.start = R._availableAnimAttrs.end = R._availableAnimAttrs.cx; + } + + // Pie 3D point class + function Pie3DManager (chart) { + var manager = this; + + manager.config = {}; + manager.linkedItems = { + chart: chart + }; + } + + Pie3DManager.prototype = { + configure: function (depth, hasOnePoint, use3DLighting, isDoughnut) { + var manager = this, + chart = manager.linkedItems.chart, + renderer = chart.get('components', 'paper'), + seriesGroup = chart.get('graphics', 'datasetGroup'); + if (isObject (depth)) { + depth = depth.depth; + hasOnePoint = depth.hasOnePoint; + use3DLighting = depth.use3DLighting; + isDoughnut = depth.isDoughnut; + } + // @todo: need to have pie to daughnut transpose + + //add the values to the instance + if (!manager.renderer) { + manager.renderer = renderer; + } + manager.hasOnePoint = hasOnePoint; + manager.use3DLighting = use3DLighting; + manager.isDoughnut = isDoughnut; + manager.depth = depth; + + //create required groups + /** @todo if requared create bottom side group */ + !manager.bottomBorderGroup && (manager.bottomBorderGroup = + renderer.group('bottom-border', seriesGroup)); + + // @todo: need to have animate with for dept + manager.bottomBorderGroup.attr ({ + transform: 't0,' + depth + }); + !manager.slicingWallsBackGroup && (manager.slicingWallsBackGroup = + renderer.group ('slicingWalls-back-Side', seriesGroup)); + !manager.slicingWallsFrontGroup && (manager.slicingWallsFrontGroup = renderer + .group ('slicingWalls-front-Side', seriesGroup)); + !manager.topGroup && (manager.topGroup = renderer.group ('top-Side', seriesGroup)); + + + // if arrays are not availabel the create the m for first time + !manager.pointElemStore && (manager.pointElemStore = []); + !manager.slicingWallsArr && (manager.slicingWallsArr = []); + + //few reusable code + manager.moveCmdArr = [M]; + manager.lineCmdArr = [L]; + manager.closeCmdArr = [Z]; + + manager.colorObjs = []; + }, + + getArcPath : function (cX, cY, startX, startY, endX, endY, rX, rY, isClockWise, isLargeArc) { + return (startX == endX && startY == endY) ? [] : [A, rX, rY, 0, isLargeArc, isClockWise, endX, endY]; + + }, + + _parseSliceColor : function (color, alpha, pointConf) { + var manager = this, + dark1, + dark2, + dark3, + dark4, + dark5, + dark6, + light1, + light2, + light3, + light4, + light5, + light6, + alpha1, + colorStr1, + colorStr2, + alphaStr1, + alphaStr2, + alphaStr3, + colorStr3, + colorStr4, + colorStr5, + alphaFactor = 3, + lighting3D = manager.use3DLighting, + cacheStore = lighting3D ? pie3DCacheColorStore.lighting3D : pie3DCacheColorStore.lighting2D, + colorsObj, + radiusYFactor = pointConf.radiusYFactor, + cx = pointConf.cx, + cy = pointConf.cy, + rx = pointConf.r, + ry = rx * radiusYFactor, + innerRx = pointConf.innerR || 0, + rightX = cx + rx, + leftX = cx - rx, + rightInnerX = cx + innerRx, + leftInnerX = cx - innerRx; + + alpha = alpha || 100; + alpha1 = alpha / 2; + + // check in cacheStore + if (cacheStore[color] && cacheStore[color][alpha]) { // if found return the color + colorsObj = cacheStore[color][alpha]; + } + else {// create the color components + if (!cacheStore[color]) { + cacheStore[color] = {}; + } + if (!cacheStore[color][alpha]) { + cacheStore[color][alpha] = {}; + } + colorsObj = cacheStore[color][alpha]; + + if (lighting3D) { + dark1 = getDarkColor(color, 80); + dark2 = getDarkColor(color, 75); + light1 = getLightColor(color, 85); + light2 = getLightColor(color, 70); + light3 = getLightColor(color, 40); + + light4 = getLightColor(color, 50); + light5 = getLightColor(color, 30); + light6 = getLightColor(color, 65); + dark3 = getDarkColor(color, 85); + dark4 = getDarkColor(color, 69); + dark5 = getDarkColor(color, 75); + dark6 = getDarkColor(color, 95); + } + else { + alphaFactor = 10; + dark1 = getDarkColor(color, 90); + dark2 = getDarkColor(color, 87); + light1 = getLightColor(color, 93); + light2 = getLightColor(color, 87); + light3 = getLightColor(color, 80); + + light6 = light4 = getLightColor(color, 85); + light5 = getLightColor(color, 80); + dark6 = dark3 = getDarkColor(color, 85); + dark4 = getDarkColor(color, 75); + dark5 = getDarkColor(color, 80); + } + colorStr1 = dark2 + COMMASTRING + light1 + COMMASTRING + light2 + + COMMASTRING + light1 + COMMASTRING + dark2; + alphaStr1 = alpha + COMMASTRING + alpha + COMMASTRING + alpha + + COMMASTRING + alpha + COMMASTRING + alpha; + colorStr2 = dark2 + COMMASTRING + color + COMMASTRING + light1 + + COMMASTRING + color + COMMASTRING + dark2; + alphaStr2 = alpha1 + COMMASTRING + alpha1 + COMMASTRING + alpha1 + + COMMASTRING + alpha1 + COMMASTRING + alpha1; + colorStr3 = dark2 + COMMASTRING + color + COMMASTRING + light3 + + COMMASTRING + color + COMMASTRING + dark2; + colorStr4 = dark5 + COMMASTRING + light1 + COMMASTRING + light4 + + COMMASTRING + light1 + COMMASTRING + dark4; + colorStr5 = 'FFFFFF' + COMMASTRING + 'FFFFFF' + COMMASTRING + 'FFFFFF' + + COMMASTRING + 'FFFFFF' + COMMASTRING + 'FFFFFF'; + alphaStr3 = 0 + COMMASTRING + alpha1/alphaFactor + COMMASTRING + alpha/alphaFactor + + COMMASTRING + alpha1/alphaFactor + COMMASTRING + 0; + + if (hasSVG) { + colorsObj.top = { + FCcolor : { + gradientUnits : 'userSpaceOnUse', + radialGradient : true, + color : light6 + COMMASTRING + dark6, + alpha : alpha + COMMASTRING + alpha, + ratio : '0,100' + } + }; + } + else { + colorsObj.top = { + FCcolor : { + gradientUnits : 'objectBoundingBox', + color : light2 + COMMASTRING + light2 + COMMASTRING + light1 + COMMASTRING + dark2, + alpha : alpha + COMMASTRING + alpha + COMMASTRING + alpha + COMMASTRING + alpha, + angle : -72, + ratio : '0,8,15,77' + } + }; + } + + colorsObj.frontOuter = { + FCcolor : { + gradientUnits : 'userSpaceOnUse', + y1 : 0, + y2 : 0, + color : colorStr4, + alpha : alphaStr1, + angle : 0, + ratio : '0,20,15,15,50' + } + }; + colorsObj.backOuter = { + FCcolor : { + gradientUnits : 'userSpaceOnUse', + y1 : 0, + y2 : 0, + color : colorStr3, + alpha : alphaStr2, + angle : 0, + ratio : '0,62,8,8,22' + } + }; + colorsObj.frontInner = { + FCcolor : { + gradientUnits : 'userSpaceOnUse', + y1 : 0, + y2 : 0, + color : colorStr2, + alpha : alphaStr2, + angle : 0, + ratio : '0,25,5,5,65' + } + }; + colorsObj.backInner = { + FCcolor : { + gradientUnits : 'userSpaceOnUse', + y1 : 0, + y2 : 0, + color : colorStr1, + alpha : alphaStr1, + angle : 0, + ratio : '0,62,8,8,22' + } + }; + colorsObj.topBorder = { + FCcolor : { + gradientUnits : 'userSpaceOnUse', + y1 : 0, + y2 : 0, + color : colorStr5, + alpha : alphaStr3, + angle : 0, + ratio : '0,20,15,15,50' + } + }; + colorsObj.topInnerBorder = { + FCcolor : { + gradientUnits : 'userSpaceOnUse', + y1 : 0, + y2 : 0, + color : colorStr5, + alpha : alphaStr3, + angle : 0, + ratio : '0,50,15,15,20' + } + }; + colorsObj.bottom = toRaphaelColor(convertColor(color, alpha1)); + /** @todo will be changed w. r. t. angle */ + colorsObj.startSlice = toRaphaelColor(convertColor(dark1, alpha)); + colorsObj.endSlice = toRaphaelColor(convertColor(dark1, alpha)); + } + + + // check if non color attributes match, else apply them + if (colorsObj.cx !== cx || colorsObj.cy !== cy || colorsObj.rx !== rx || + colorsObj.radiusYFactor !== radiusYFactor || colorsObj.innerRx !== innerRx) { + // appaly positional properties in colors + if (hasSVG) { + colorsObj.top.FCcolor.cx = cx; + colorsObj.top.FCcolor.cy = cy; + colorsObj.top.FCcolor.r = rx; + colorsObj.top.FCcolor.fx = cx - 0.3 * rx; + colorsObj.top.FCcolor.fy = cy + ry * 1.2; + } + colorsObj.topBorder.FCcolor.x1 = colorsObj.backOuter.FCcolor.x1 = colorsObj.frontOuter.FCcolor.x1 = + leftX; + colorsObj.topBorder.FCcolor.x2 = colorsObj.backOuter.FCcolor.x2 = colorsObj.frontOuter.FCcolor.x2 = + rightX; + colorsObj.topInnerBorder.FCcolor.x1 = colorsObj.backInner.FCcolor.x1 = + colorsObj.frontInner.FCcolor.x1 = leftInnerX; + colorsObj.topInnerBorder.FCcolor.x2 = colorsObj.backInner.FCcolor.x2 = + colorsObj.frontInner.FCcolor.x2 = rightInnerX; + + + + // Set positional attributes in color Object + colorsObj.cx = cx; + colorsObj.cy = cy; + colorsObj.rx = rx; + colorsObj.radiusYFactor = radiusYFactor; + colorsObj.innerRx = innerRx; + + } + + return colorsObj; + }, + + rotate : function (angle) { + var manager = this, + pointElemStore = manager.pointElemStore, + x = 0, ln = pointElemStore.length, point, confObject; + if (!manager.hasOnePoint) { + for (; x < ln; x += 1) { + point = pointElemStore[x]; + confObject = point._confObject; + confObject.start += angle; + confObject.end += angle; + manager._setSliceShape(confObject); + } + manager.refreshDrawing(); + } + }, + + removeSlice: function (slice) { + var manager = this, + pointElemStore = manager.pointElemStore, + confObject = slice._confObject, + elements = confObject.elements, + slicingWallsArr = manager.slicingWallsArr, + x, + ln = pointElemStore.length, + point, + wallElement; + // remove the slice from the point store + for (x = ln - 1; x >= 0; x -= 1) { + point = pointElemStore[x]; + if (point === slice) { + pointElemStore.splice(x,1); + } + } + // remove the side elements from the side wall arr + ln = slicingWallsArr.length; + for (x = ln - 1; x >= 0; x -= 1) { + wallElement = slicingWallsArr[x]; + if (wallElement === elements.startSlice || wallElement === elements.frontOuter1 || + wallElement === elements.frontOuter || wallElement === elements.backInner || + wallElement === elements.endSlice) { + slicingWallsArr.splice(x, 1); + } + } + + // hide corrosponding elements + slice.hide && slice.hide(); + + // store the element in pool for future reuse + if (!manager._slicePool) { + manager._slicePool = []; + } + manager._slicePool.push(slice); + + manager.refreshDrawing(); + }, + useSliceFromPool: function () { + var manager = this, + slicePool = manager._slicePool || (manager._slicePool = []), + slicingWallsArr = manager.slicingWallsArr, + newSlice = false, + elements; + if (slicePool.length) { + newSlice = slicePool.shift(); + // restore the element in point store + manager.pointElemStore.push(newSlice); + // show the slice + newSlice.show(); + // push the side walls in to the side wall array + elements = newSlice._confObject.elements; + slicingWallsArr.push(elements.startSlice, elements.frontOuter1, elements.frontOuter); + + elements.backInner && slicingWallsArr.push(elements.backInner); + slicingWallsArr.push(elements.endSlice); + } + return newSlice; + }, + // This function do the z-index management + refreshDrawing : (function () { + var elementZSortFn = function (a, b) { + var centerAngleDiff = (a._conf.index - b._conf.index) || (a._conf.cIndex - b._conf.cIndex) || + (a._conf.isStart - b._conf.isStart) || (a._conf.si - b._conf.si); + //!centerAngleDiff && console.log("aaa"); + return centerAngleDiff; + }, + getStartIndex = function (array) { + var l, + i, + startsAtFrontHalf, + startIndex = array[0] && array[0]._conf.index, + atFrontHalf, + index; + + startsAtFrontHalf = startIndex <= pi; + for (i = 1, l = array.length; i < l; i += 1) { + index = array[i]._conf.index; + atFrontHalf = index <= pi; + if (atFrontHalf != startsAtFrontHalf || index < startIndex) { + return i; + } + } + return 0; + }; + + return function () { + var slicingWallsArr = this.slicingWallsArr, + x = 0, + sWall, + ln = slicingWallsArr.length, + startIndex, + lastElem2, + lastElem3, + index, + frontGroup = this.slicingWallsFrontGroup, + backGroup = this.slicingWallsBackGroup; + + // sort the slicing walls for z-placing + slicingWallsArr.sort(elementZSortFn); + // find the index which cross the pi line + startIndex = getStartIndex(slicingWallsArr); + + for (; x < ln; x += 1, startIndex += 1) { + if (startIndex === ln) { + startIndex = 0; + } + sWall = slicingWallsArr[startIndex], index = sWall._conf.index; + if (index < piBy2) { + frontGroup.appendChild(sWall); + } + else if (index <= pi) { + if (lastElem2) { + sWall.insertBefore(lastElem2); + } + else { + frontGroup.appendChild(sWall); + } + lastElem2 = sWall; + } + else if (index <= pi3By2) { + if (lastElem3) { + sWall.insertBefore(lastElem3); + } + else { + backGroup.appendChild(sWall); + } + lastElem3 = sWall; + } + else{ + backGroup.appendChild(sWall); + } + } + }; + })(), + // @todo: needs to be optimize + _setSliceShape : function (pointConf, doNotApply) { + var Pie3DManager = this, + getArcPath = Pie3DManager.getArcPath, + startOri = pointConf.start, endOri = pointConf.end, + start = normalizeAngle(startOri), + end = normalizeAngle(endOri), + scaleAngle, + startCos, startSin, endCos, endSin, startOuterX, startOuterY, + startOuterTopClipX, startOuterTopClipY, endOuterTopClipX, endOuterTopClipY, + startInnerX, startInnerY, endInnerX, endInnerY, startInnerY1, endInnerY1, + borderThickness = 1, + isDoughnut = Pie3DManager.isDoughnut, + radiusYFactor = pointConf.radiusYFactor, + cx = pointConf.cx, + cy = pointConf.cy, + rx = pointConf.r, + ry = rx * radiusYFactor, + topCliprx = rx + (hasSVG ? -borderThickness : 2), + topClipry = ry + (hasSVG ? -borderThickness : 2), + innerRx = pointConf.innerR || 0, + innerRy = innerRx * radiusYFactor, + depth = Pie3DManager.depth, + depthY = depth + cy, + rightX = cx + rx, + leftX = cx - rx, + rightInnerX = cx + innerRx, + leftInnerX = cx - innerRx, + topY = cy - ry, + bottomY = depthY + ry, + clipPathforNoClip = [M, leftInnerX, topY, L, leftInnerX, bottomY, Z], + elements = pointConf.elements, + startOuterY1, endOuterX, endOuterY, endOuterY1, + tempArr1, tempArr2, tempArr3, tempArr4, tempArr5, tempArr6, + moveCmdArr, + lineCmdArr, + closeCmdArr, + centerPoint, + leftPoint, + topPoint, + rightPoint, + bottomPoint, + leftDepthPoint, + rightDepthPoint, + leftInnerPoint, + rightInnerPoint, + leftInnerDepthPoint, + rightInnerDepthPoint, + pathAttrString = 'path', + middleAngle = (start + end) / 2, // not applicable for the slices that are crossing the 0deg + frontOuterIndex, + BackOuterIndex, + crossed2Pi = start > end; + + startCos = mathCos(start); + startSin = mathSin(start); + endCos = mathCos(end); + endSin = mathSin(end); + + startOuterX = cx + (rx * startCos); + startOuterY = cy + (ry * startSin); + startOuterTopClipX = cx + (topCliprx * startCos); + startOuterTopClipY = cy + (topClipry * startSin); + startOuterY1 = startOuterY + depth; + endOuterX = cx + (rx * endCos); + endOuterY = cy + (ry * endSin); + endOuterTopClipX = cx + (topCliprx * endCos); + endOuterTopClipY = cy + (topClipry * endSin); + endOuterY1 = endOuterY + depth; + + if (isDoughnut) {//doughnut like slice + startInnerX = cx + (innerRx * startCos); + startInnerY = cy + (innerRy * startSin); + startInnerY1 = startInnerY + depth; + endInnerX = cx + (innerRx * endCos); + endInnerY = cy + (innerRy * endSin); + endInnerY1 = endInnerY + depth; + pointConf.startSlice = [M, startOuterX, startOuterY, L, startOuterX, startOuterY1, startInnerX, + startInnerY1, startInnerX, startInnerY, Z]; + pointConf.endSlice = [M, endOuterX, endOuterY, L, endOuterX, endOuterY1, endInnerX, endInnerY1, + endInnerX, endInnerY, Z]; + } + else { + pointConf.startSlice = [M, startOuterX, startOuterY, L, startOuterX, startOuterY1, cx, depthY, cx, + cy, Z]; + pointConf.endSlice = [M, endOuterX, endOuterY, L, endOuterX, endOuterY1, cx, depthY, cx, cy, Z]; + } + + if (hasSVG) { + scaleAngle = getAbsScaleAngle (start, end); + //create the clip for top and bottom + if (!isDoughnut) { + pointConf.clipTopPath = [[M, startOuterX, startOuterY, + A, rx, ry, 0, (scaleAngle > pi ? 1 : 0), 1, endOuterX, endOuterY, + L, cx, cy, + Z]]; + } + else { + pointConf.clipTopPath = [[M, startOuterX, startOuterY, A, rx, ry, 0, (scaleAngle > pi ? 1 : 0), + 1, endOuterX, endOuterY, L, endInnerX, endInnerY, A, innerRx, innerRy, + 0, (scaleAngle > pi ? 1 : 0), 0, startInnerX, startInnerY, Z]]; + } + + pointConf.clipOuterFrontPath1 = [clipPathforNoClip]; + + pointConf.clipTopBorderPath = [[M, startOuterTopClipX, startOuterTopClipY, A, topCliprx, topClipry, + 0, (scaleAngle > pi ? 1 : 0), 1, endOuterTopClipX, endOuterTopClipY, + L, endOuterX, endOuterY, endOuterX, endOuterY + borderThickness, A, + rx, ry, 0, (scaleAngle > pi ? 1 : 0), 0, startOuterX, startOuterY + + borderThickness, L, startOuterX, startOuterY, Z]]; + + if (startOri != endOri) { + if(start > end) {//crossed the 0 deg line + if (start < pi){//crossed the 180 deg line also + pointConf.clipOuterFrontPath = [[M, rightX, cy, + A, rx, ry, 0, 0, 1, endOuterX, endOuterY, + V, depth, + A, rx, ry, 0, 0, 0, rightX, cy + depth, + Z]]; + pointConf.clipOuterFrontPath1 = [[M, leftX, cy, + A, rx, ry, 0, 0, 0, startOuterX, startOuterY, + V, depth, + A, rx, ry, 0, 0, 1, leftX, cy + depth, + Z]]; + pointConf.clipOuterBackPath = [[M, rightX, cy, + A, rx, ry, 0, 1, 0, leftX, cy, + V, depth, + A, rx, ry, 0, 1, 1, rightX, cy + depth, + Z]]; + if (isDoughnut) { + pointConf.clipInnerBackPath = [[M, rightInnerX, cy, + A, innerRx, innerRy, 0, 1, 0, leftInnerX, cy, + V, depth, + A, innerRx, innerRy, 0, 1, 1, rightInnerX, cy + + depth, Z]]; + + pointConf.clipInnerFrontPath = [[M, rightInnerX, cy, + A, innerRx, innerRy, 0, 0, 1, endInnerX, endInnerY, + V, depth, + A, innerRx, innerRy, 0, 0, 0, rightInnerX, cy + + depth, Z, + M, leftInnerX, cy, + A, innerRx, innerRy, 0, 0, 0, startInnerX, + startInnerY, V, depth, + A, innerRx, innerRy, 0, 0, 1, leftInnerX, cy + + depth, Z]]; + + } + } + else if( end > pi) {//crossed the 180 deg line also + pointConf.clipOuterFrontPath = [[M, rightX, cy, + A, rx, ry, 0, 1, 1, leftX, cy, + V, depth, + A, rx, ry, 0, 1, 0, rightX, cy + depth, + Z]]; + pointConf.clipOuterBackPath = [[M, leftX, cy, + A, rx, ry, 0, 0, 1, endOuterX, endOuterY, + V, depth, + A, rx, ry, 0, 0, 0, leftX, cy + depth, + Z, + M, rightX, cy, + A, rx, ry, 0, 0, 0, startOuterX, startOuterY, + V, depth, + A, rx, ry, 0, 0, 1, rightX, cy + depth, + Z]]; + if (isDoughnut) { + + pointConf.clipInnerFrontPath = [[M, rightInnerX, cy, + A, innerRx, innerRy, 0, 1, 1, leftInnerX, cy, + V, depth, + A, innerRx, innerRy, 0, 1, 0, rightInnerX, cy + + depth, Z]]; + + pointConf.clipInnerBackPath = [[M, leftInnerX, cy, + A, innerRx, innerRy, 0, 0, 1, endInnerX, endInnerY, + V, depth, + A, innerRx, innerRy, 0, 0, 0, leftInnerX, cy + + depth, Z, + M, rightInnerX, cy, + A, innerRx, innerRy, 0, 0, 0, startInnerX, + startInnerY, V, depth, + A, innerRx, innerRy, 0, 0, 1, rightInnerX, cy + + depth, Z]]; + } + } + else { + pointConf.clipOuterFrontPath = [[M, rightX, cy, + A, rx, ry, 0, 0, 1, endOuterX, endOuterY, + V, depth, + A, rx, ry, 0, 0, 0, rightX, cy + depth, + Z]]; + pointConf.clipOuterBackPath = [[M, startOuterX, startOuterY, + A, rx, ry, 0, 0, 1, rightX, cy, + V, depth, + A, rx, ry, 0, 0, 0, startOuterX, startOuterY1, + Z]]; + if (isDoughnut) { + + pointConf.clipInnerFrontPath = [[M, rightInnerX, cy, + A, innerRx, innerRy, 0, 0, 1, endInnerX, endInnerY, + V, depth, + A, innerRx, innerRy, 0, 0, 0, rightInnerX, cy + + depth, Z]]; + + pointConf.clipInnerBackPath = [[M, startInnerX, startInnerY, + A, innerRx, innerRy, 0, 0, 1, rightInnerX, cy, + V, depth, + A, innerRx, innerRy, 0, 0, 0, startInnerX, + startInnerY1, Z]]; + } + } + } + else if (start < pi){ + if (end > pi) {//crossed the 180 deg line only + pointConf.clipOuterFrontPath = [[M, startOuterX, startOuterY, + A, rx, ry, 0, 0, 1, leftX, cy, + V, depth, + A, rx, ry, 0, 0, 0, startOuterX, startOuterY1, + Z]]; + pointConf.clipOuterBackPath = [[M, leftX, cy, + A, rx, ry, 0, 0, 1, endOuterX, endOuterY, + V, depth, + A, rx, ry, 0, 0, 0, leftX, cy + depth, + Z]]; + if (isDoughnut) { + pointConf.clipInnerFrontPath = [[M, startInnerX, startInnerY, + A, innerRx, innerRy, 0, 0, 1, leftInnerX, + cy, V, depth, + A, innerRx, innerRy, 0, 0, 0, startInnerX, + startInnerY1, Z]]; + pointConf.clipInnerBackPath = [[M, leftInnerX, cy, + A, innerRx, innerRy, 0, 0, 1, endInnerX, + endInnerY, V, depth, + A, innerRx, innerRy, 0, 0, 0, leftInnerX, + cy + depth, Z]]; + } + } + else {//haven't crossed any thing + pointConf.clipOuterFrontPath = [[M, startOuterX, startOuterY, + A, rx, ry, 0, 0, 1, endOuterX, endOuterY, + V, depth, + A, rx, ry, 0, 0, 0, startOuterX, startOuterY1, + Z]]; + pointConf.clipOuterBackPath = [clipPathforNoClip]; + + if (isDoughnut) { + + pointConf.clipInnerFrontPath = [[M, startInnerX, startInnerY, + A, innerRx, innerRy, 0, 0, 1, endInnerX, endInnerY, + V, depth, + A, innerRx, innerRy, 0, 0, 0, startInnerX, + startInnerY1, Z]]; + + pointConf.clipInnerBackPath = [clipPathforNoClip]; + + } + } + } + else {//haven't crossed any thing + pointConf.clipOuterFrontPath = [clipPathforNoClip]; + pointConf.clipOuterBackPath = [[M, startOuterX, startOuterY, + A, rx, ry, 0, 0, 1, endOuterX, endOuterY, + V, depth, + A, rx, ry, 0, 0, 0, startOuterX, startOuterY1, + Z]]; + if (isDoughnut) { + + pointConf.clipInnerFrontPath = [clipPathforNoClip]; + + pointConf.clipInnerBackPath = [[M, startInnerX, startInnerY, + A, innerRx, innerRy, 0, 0, 1, endInnerX, endInnerY, + V, depth, + A, innerRx, innerRy, 0, 0, 0, startInnerX, startInnerY1, + Z]]; + } + } + } + else {//zero Pie + pointConf.clipOuterFrontPath = pointConf.clipOuterBackPath = pointConf.clipInnerBackPath = + pointConf.clipInnerFrontPath = [clipPathforNoClip]; + } + + pathAttrString = 'litepath'; + pointConf.clipBottomBorderPath = pointConf.clipTopPath; + pointConf.startSlice = [pointConf.startSlice]; + pointConf.endSlice = [pointConf.endSlice]; + } + else {//for VML + moveCmdArr = Pie3DManager.moveCmdArr; + lineCmdArr = Pie3DManager.lineCmdArr; + closeCmdArr = Pie3DManager.closeCmdArr; + centerPoint = [cx, cy]; + leftPoint = [leftX, cy]; + topPoint = [cx, topY]; + rightPoint = [rightX, cy]; + bottomPoint = [cx, cy + ry]; + leftDepthPoint = [leftX, depthY]; + rightDepthPoint = [rightX, depthY]; + leftInnerPoint = [leftInnerX, cy]; + rightInnerPoint = [rightInnerX, cy]; + leftInnerDepthPoint = [leftInnerX, depthY]; + rightInnerDepthPoint = [rightInnerX, depthY]; + pointConf.clipOuterFrontPath1 = []; + if (startOri != endOri) { + if(start > end) {//crossed the 0 deg line + if (start < pi){//crossed the 180 deg line also + tempArr1 = getArcPath(cx, cy, startOuterX, startOuterY, leftX, cy, rx, ry, 1, 0); + tempArr3 = getArcPath(cx, cy, leftX, cy, rightX, cy, rx, ry, 1, 0); + tempArr5 = getArcPath(cx, cy, rightX, cy, endOuterX, endOuterY, rx, ry, 1, 0); + pointConf.clipOuterBackPath = moveCmdArr.concat(leftPoint, tempArr3, lineCmdArr, + rightDepthPoint, getArcPath(cx, depthY, rightX, + depthY, leftX, depthY, rx, ry, 0, 0), closeCmdArr); + pointConf.clipOuterFrontPath1 = moveCmdArr.concat( [startOuterX, startOuterY], tempArr1, + lineCmdArr, leftDepthPoint, getArcPath(cx, depthY, leftX, depthY, startOuterX, + startOuterY1, rx, ry, 0, 0), closeCmdArr); + pointConf.clipOuterFrontPath = moveCmdArr.concat(rightPoint, + tempArr5, lineCmdArr, [endOuterX, endOuterY1], getArcPath(cx, depthY, endOuterX, + endOuterY1, rightX, depthY, rx, ry, 0, 0), closeCmdArr); + pointConf.clipTopBorderPath = moveCmdArr.concat([startOuterX, startOuterY], tempArr1, + tempArr3, tempArr5); + if (isDoughnut) { + tempArr2 = getArcPath(cx, cy, endInnerX, endInnerY, rightInnerX, cy, innerRx, + innerRy, 0, 0); + tempArr4 = getArcPath(cx, cy, rightInnerX, cy, leftInnerX, cy, innerRx, + innerRy, 0, 0); + tempArr6 = getArcPath(cx, cy, leftInnerX, cy, startInnerX, startInnerY, + innerRx, innerRy, 0, 0); + pointConf.clipInnerBackPath = moveCmdArr.concat(rightInnerPoint, tempArr4, + lineCmdArr, leftInnerDepthPoint, + getArcPath(cx, depthY, leftInnerX, depthY, rightInnerX, depthY, + innerRx, innerRy, 1, 0), closeCmdArr); + pointConf.clipInnerFrontPath = moveCmdArr.concat(leftInnerPoint, tempArr6, + lineCmdArr, [startInnerX, startInnerY1], getArcPath(cx, depthY, startInnerX, + startInnerY1, leftInnerX, depthY, innerRx, innerRy, 1, 0), closeCmdArr, + moveCmdArr, + [endInnerX, endInnerY], tempArr2, lineCmdArr, rightInnerDepthPoint, + getArcPath(cx, depthY, rightInnerX, depthY, endInnerX, endInnerY1, + innerRx, innerRy, 1, 0), closeCmdArr); + pointConf.clipTopPath = pointConf.clipTopBorderPath.concat(lineCmdArr, [endInnerX, + endInnerY], tempArr2, tempArr4, tempArr6, closeCmdArr); + pointConf.clipTopBorderPath = pointConf.clipTopBorderPath.concat(moveCmdArr, + [endInnerX, endInnerY], tempArr2, tempArr4, tempArr6); + + } + else { + pointConf.clipTopPath = pointConf.clipTopBorderPath.concat(lineCmdArr, centerPoint, + closeCmdArr); + } + } + else if( end > pi) {//crossed the 180 deg line also + tempArr1 = getArcPath(cx, cy, startOuterX, startOuterY, rightX, cy, rx, ry, 1, 0); + tempArr3 = getArcPath(cx, cy, rightX, cy, leftX, cy, rx, ry, 1, 0); + tempArr5 = getArcPath(cx, cy, leftX, cy, endOuterX, endOuterY, rx, ry, 1, 0); + pointConf.clipOuterFrontPath = moveCmdArr.concat(rightPoint, tempArr3, lineCmdArr, + leftDepthPoint, + getArcPath(cx, depthY, leftX, + depthY, rightX, depthY, rx, ry, 0, 0), closeCmdArr); + pointConf.clipOuterBackPath = moveCmdArr.concat( [startOuterX, startOuterY], tempArr1, + lineCmdArr, + rightDepthPoint, getArcPath(cx, depthY, rightX, + depthY, startOuterX, startOuterY1, rx, ry, 0, 0), closeCmdArr, moveCmdArr, + leftPoint, + tempArr5, lineCmdArr, [endOuterX, endOuterY1], getArcPath(cx, depthY, endOuterX, + endOuterY1, leftX, depthY, rx, ry, 0, 0), closeCmdArr); + pointConf.clipTopBorderPath = moveCmdArr.concat([startOuterX, startOuterY], tempArr1, + tempArr3, tempArr5); + if (isDoughnut) { + tempArr2 = getArcPath(cx, cy, endInnerX, endInnerY, leftInnerX, cy, innerRx, + innerRy, 0, 0); + tempArr4 = getArcPath(cx, cy, leftInnerX, cy, rightInnerX, cy, innerRx, + innerRy, 0, 0); + tempArr6 = getArcPath(cx, cy, rightInnerX, cy, startInnerX, startInnerY, + innerRx, innerRy, 0, 0); + pointConf.clipInnerFrontPath = moveCmdArr.concat(leftInnerPoint, tempArr4, + lineCmdArr, rightInnerDepthPoint, + getArcPath(cx, depthY, rightInnerX, depthY, leftInnerX, depthY, + innerRx, innerRy, 1, 0), closeCmdArr); + pointConf.clipInnerBackPath = moveCmdArr.concat(rightInnerPoint, tempArr6, + lineCmdArr, [startInnerX, startInnerY1], getArcPath(cx, depthY, startInnerX, + startInnerY1, rightInnerX, depthY, innerRx, innerRy, 1, 0), + closeCmdArr, moveCmdArr, [endInnerX, endInnerY], tempArr2, lineCmdArr, + leftInnerDepthPoint, getArcPath(cx, depthY, leftInnerX, depthY, + endInnerX, endInnerY1, innerRx, innerRy, 1, 0), closeCmdArr); + + pointConf.clipTopPath = pointConf.clipTopBorderPath.concat(lineCmdArr, [endInnerX, + endInnerY], tempArr2, tempArr4, tempArr6, closeCmdArr); + pointConf.clipTopBorderPath = pointConf.clipTopBorderPath.concat(moveCmdArr, + [endInnerX, endInnerY], tempArr2, tempArr4, tempArr6); + + } + else { + pointConf.clipTopPath = pointConf.clipTopBorderPath.concat(lineCmdArr, centerPoint, + closeCmdArr); + } + } + else { + tempArr1 = getArcPath(cx, cy, startOuterX, startOuterY, rightX, cy, rx, ry, 1, 0); + tempArr3 = getArcPath(cx, cy, rightX, cy, endOuterX, endOuterY, rx, ry, 1, 0); + pointConf.clipOuterFrontPath = moveCmdArr.concat(rightPoint, tempArr3, lineCmdArr, + [endOuterX, endOuterY1], getArcPath(cx, depthY, endOuterX, endOuterY1, rightX, + depthY, rx, ry, 0, 0), closeCmdArr); + pointConf.clipOuterBackPath = moveCmdArr.concat( [startOuterX, startOuterY], tempArr1, + lineCmdArr, rightDepthPoint, getArcPath(cx, depthY, rightX, depthY, + startOuterX, startOuterY1, rx, ry, 0, 0), closeCmdArr); + pointConf.clipTopBorderPath = moveCmdArr.concat([startOuterX, startOuterY], tempArr1, + tempArr3); + if (isDoughnut) { + tempArr2 = getArcPath(cx, cy, endInnerX, endInnerY, rightInnerX, cy, innerRx, + innerRy, 0, 0); + tempArr4 = getArcPath(cx, cy, rightInnerX, cy, startInnerX, startInnerY, + innerRx, innerRy, 0, 0); + pointConf.clipInnerFrontPath = moveCmdArr.concat([endInnerX, endInnerY], tempArr2, + lineCmdArr, rightInnerDepthPoint, + getArcPath(cx, depthY, rightInnerX, depthY, endInnerX, endInnerY1, innerRx, + innerRy, 1, 0), closeCmdArr); + pointConf.clipInnerBackPath = moveCmdArr.concat(rightInnerPoint, tempArr4, + lineCmdArr, [startInnerX, startInnerY1], getArcPath(cx, depthY, startInnerX, + startInnerY1, rightInnerX, depthY, innerRx, innerRy, 1, 0), + closeCmdArr); + pointConf.clipTopPath = pointConf.clipTopBorderPath.concat(lineCmdArr, [endInnerX, + endInnerY], tempArr2, tempArr4, closeCmdArr); + pointConf.clipTopBorderPath = pointConf.clipTopBorderPath.concat(moveCmdArr, + [endInnerX, endInnerY], tempArr2, tempArr4); + + } + else { + pointConf.clipTopPath = pointConf.clipTopBorderPath.concat(lineCmdArr, centerPoint, + closeCmdArr); + } + } + } + else if (start < pi){ + if (end > pi) {//crossed the 180 deg line only + tempArr1 = getArcPath(cx, cy, startOuterX, startOuterY, leftX, cy, rx, ry, 1, 0); + tempArr3 = getArcPath(cx, cy, leftX, cy, endOuterX, endOuterY, rx, ry, 1, 0); + pointConf.clipOuterBackPath = moveCmdArr.concat(leftPoint, tempArr3, lineCmdArr, + [endOuterX, endOuterY1], + getArcPath(cx, depthY, endOuterX, + endOuterY1, leftX, depthY, rx, ry, 0, 0), closeCmdArr); + pointConf.clipOuterFrontPath = moveCmdArr.concat( [startOuterX, startOuterY], tempArr1, + lineCmdArr, + leftDepthPoint, getArcPath(cx, depthY, leftX, + depthY, startOuterX, startOuterY1, rx, ry, 0, 0), closeCmdArr); + pointConf.clipTopBorderPath = moveCmdArr.concat([startOuterX, startOuterY], tempArr1, + tempArr3); + if (isDoughnut) { + tempArr2 = getArcPath(cx, cy, endInnerX, endInnerY, leftInnerX, cy, innerRx, + innerRy, 0, 0); + tempArr4 = getArcPath(cx, cy, leftInnerX, cy, startInnerX, startInnerY, + innerRx, innerRy, 0, 0); + pointConf.clipInnerBackPath = moveCmdArr.concat([endInnerX, endInnerY], tempArr2, + lineCmdArr, leftInnerDepthPoint, + getArcPath(cx, depthY, leftInnerX, depthY, endInnerX, endInnerY1, innerRx, + innerRy, 1, 0), closeCmdArr); + pointConf.clipInnerFrontPath = moveCmdArr.concat(leftInnerPoint, tempArr4, + lineCmdArr, [startInnerX, startInnerY1], getArcPath(cx, depthY, startInnerX, + startInnerY1, leftInnerX, depthY, innerRx, innerRy, 1, 0), + closeCmdArr); + pointConf.clipTopPath = pointConf.clipTopBorderPath.concat(lineCmdArr, [endInnerX, + endInnerY], tempArr2, tempArr4, closeCmdArr); + pointConf.clipTopBorderPath = pointConf.clipTopBorderPath.concat(moveCmdArr, + [endInnerX, endInnerY], tempArr2, tempArr4); + + } + else { + pointConf.clipTopPath = pointConf.clipTopBorderPath.concat(lineCmdArr, centerPoint, + closeCmdArr); + } + } + else {//haven't crossed any thing + tempArr1 = getArcPath(cx, cy, startOuterX, startOuterY, endOuterX, endOuterY, rx, ry, 1, + 0); + pointConf.clipOuterBackPath = moveCmdArr.concat([startOuterX, startOuterY]); + pointConf.clipTopBorderPath = pointConf.clipOuterBackPath.concat(tempArr1); + pointConf.clipOuterFrontPath = pointConf.clipTopBorderPath.concat( lineCmdArr, + [endOuterX, endOuterY1], getArcPath(cx, depthY, endOuterX, + endOuterY1, startOuterX, startOuterY1, rx, ry, 0, 0), closeCmdArr); + if (isDoughnut) { + tempArr2 = getArcPath(cx, cy, endInnerX, endInnerY, startInnerX, startInnerY, + innerRx, innerRy, 0, 0); + pointConf.clipInnerBackPath = moveCmdArr.concat([endInnerX, endInnerY]); + pointConf.clipTopPath = pointConf.clipTopBorderPath.concat(lineCmdArr, [endInnerX, + endInnerY], tempArr2, closeCmdArr); + pointConf.clipTopBorderPath = pointConf.clipTopBorderPath.concat(moveCmdArr, + [endInnerX, endInnerY], tempArr2); + pointConf.clipInnerFrontPath = pointConf.clipInnerBackPath.concat(tempArr2, + lineCmdArr, [startInnerX, startInnerY1], getArcPath(cx, depthY, startInnerX, + startInnerY1, endInnerX, endInnerY1, innerRx, innerRy, 1, 0), closeCmdArr); + + } + else { + pointConf.clipTopPath = pointConf.clipTopBorderPath.concat(lineCmdArr, centerPoint, + closeCmdArr); + } + } + } + else {//haven't crossed any thing + tempArr1 = getArcPath(cx, cy, startOuterX, startOuterY, endOuterX, endOuterY, rx, ry, 1, 0); + pointConf.clipOuterFrontPath = moveCmdArr.concat([startOuterX, startOuterY]); + pointConf.clipTopBorderPath = pointConf.clipOuterFrontPath.concat(tempArr1); + pointConf.clipOuterBackPath = pointConf.clipTopBorderPath.concat( lineCmdArr, + [endOuterX, endOuterY1], getArcPath(cx, depthY, endOuterX, + endOuterY1, startOuterX, startOuterY1, rx, ry, 0, 0), closeCmdArr); + if (isDoughnut) { + tempArr2 = getArcPath(cx, cy, endInnerX, endInnerY, startInnerX, startInnerY, innerRx, + innerRy, 0, 0); + pointConf.clipInnerFrontPath = moveCmdArr.concat([endInnerX, endInnerY]); + pointConf.clipTopPath = pointConf.clipTopBorderPath.concat(lineCmdArr, [endInnerX, + endInnerY], tempArr2, closeCmdArr); + pointConf.clipTopBorderPath = pointConf.clipTopBorderPath.concat( + pointConf.clipInnerFrontPath, tempArr2); + pointConf.clipInnerBackPath = pointConf.clipInnerFrontPath.concat(tempArr2, + lineCmdArr, [startInnerX, startInnerY1], getArcPath(cx, depthY, startInnerX, + startInnerY1, endInnerX, endInnerY1, innerRx, innerRy, 1, 0), closeCmdArr); + + } + else { + pointConf.clipTopPath = pointConf.clipTopBorderPath.concat(lineCmdArr, centerPoint, + closeCmdArr); + } + } + //enlarge the bounded box so that the gradient works perfactly + tempArr1 = moveCmdArr.concat(leftPoint, lineCmdArr, rightPoint); + tempArr2 = moveCmdArr.concat(topPoint, lineCmdArr, bottomPoint); + pointConf.clipTopPath = pointConf.clipTopPath.concat(tempArr1, tempArr2); + pointConf.clipOuterFrontPath = pointConf.clipOuterFrontPath.concat(tempArr1); + pointConf.clipOuterFrontPath1 = pointConf.clipOuterFrontPath1.concat(tempArr1); + pointConf.clipOuterBackPath = pointConf.clipOuterBackPath.concat(tempArr1); + + if (isDoughnut) { + tempArr2 = moveCmdArr.concat(leftInnerPoint, lineCmdArr, rightInnerPoint); + pointConf.clipInnerFrontPath = pointConf.clipInnerFrontPath.concat(tempArr2); + pointConf.clipInnerBackPath = pointConf.clipInnerBackPath.concat(tempArr2); + } + } + else {//zero Pie + pointConf.clipTopPath = + pointConf.clipOuterFrontPath = + pointConf.clipOuterBackPath = []; + if (isDoughnut) { + pointConf.clipInnerFrontPath = + pointConf.clipInnerBackPath = []; + } + } + + pointConf.clipBottomBorderPath = pointConf.clipTopBorderPath; + } + + //now apply the changes + if (!doNotApply) { + elements.startSlice._conf.index = start; + elements.endSlice._conf.index = end; + elements.backOuter._conf.index = BackOuterIndex = + (crossed2Pi && (start <= pi3By2 || end > pi3By2)) || + (start <= pi3By2 && end > pi3By2) ? pi3By2 : (start > pi ? start : end); + + elements.frontOuter._conf.index = frontOuterIndex = end <= piBy2 ? end : ((start > end || + start <= piBy2) ? piBy2 : start); + elements.frontOuter1._conf.index = start; + elements.frontOuter1._conf.cIndex = pi; + if (start > end) { + elements.backOuter._conf.cIndex = start < pi3By2 ? pi3By2 : pi2; + elements.startSlice._conf.cIndex = start < pi ? (start + pi) / 2 : (start + pi2) / 2; + elements.endSlice._conf.cIndex = elements.frontOuter._conf.cIndex = 0; + } + else { + elements.backOuter._conf.cIndex = elements.startSlice._conf.cIndex = + elements.endSlice._conf.cIndex = elements.frontOuter._conf.cIndex = middleAngle; + } + + if (scaleAngle > pi) { + elements.frontOuter1.show().attr( + pathAttrString, pointConf.clipOuterFrontPath1 + ); + } + else { + elements.frontOuter1.hide(); + } + pointConf.thisElement._attr( + pathAttrString, pointConf.clipTopPath + ); + elements.bottom.attr( + pathAttrString, pointConf.clipTopPath + ); + elements.bottomBorder.attr( + pathAttrString, pointConf.clipBottomBorderPath + ); + + elements.topBorder && elements.topBorder.attr( + pathAttrString, pointConf.clipTopBorderPath + ); + elements.frontOuter.attr( + pathAttrString, pointConf.clipOuterFrontPath + ); + elements.backOuter.attr( + pathAttrString, pointConf.clipOuterBackPath + ); + + if (isDoughnut) { + elements.backInner.attr( + pathAttrString, pointConf.clipInnerBackPath + ); + elements.frontInner.attr( + pathAttrString, pointConf.clipInnerFrontPath + ); + elements.backInner._conf.index = BackOuterIndex; + elements.frontInner._conf.index = frontOuterIndex; + if (start > end) { + elements.backInner._conf.cIndex = pi2; + elements.frontInner._conf.cIndex = 0; + } + else { + elements.backInner._conf.cIndex = + elements.frontInner._conf.cIndex = middleAngle; + } + } + + if (Pie3DManager.hasOnePoint) { + elements.startSlice.hide(); + elements.endSlice.hide(); + } + else { + elements.startSlice.attr( + pathAttrString, pointConf.startSlice + ).show(); + elements.endSlice.attr( + pathAttrString, pointConf.endSlice + ).show(); + } + } + }, + + _setSliceCosmetics: function (pointConf) { + var manager = this, + topElement = pointConf.thisElement, + showBorderEffect = pointConf.showBorderEffect, + elements = pointConf.elements, + colorObj, + borderColor = convertColor(pointConf.borderColor, pluckNumber(pointConf.borderAlpha, + pointConf.alpha)), + borderWidth = pointConf.borderWidth, + topAttrObj; + if (!pointConf.color) { + return; + } + colorObj = manager._parseSliceColor(pointConf.color, pointConf.alpha, pointConf); + if (hasSVG) { + topAttrObj = {fill: toRaphaelColor(colorObj.top), 'stroke-width': 0}; + + if (showBorderEffect) { + elements.topBorder.show().attr({ + fill: toRaphaelColor(colorObj.topBorder), + 'stroke-width': 0 + }); + } + else { + elements.topBorder.hide(); + topAttrObj.stroke = borderColor; + topAttrObj['stroke-width'] = borderWidth; + } + + // top + topElement._attr(topAttrObj); + } + else { + topElement._attr({ + fill: toRaphaelColor(colorObj.top), + 'stroke-width': 0 + }); + // top border + elements.topBorder.attr({ + stroke: borderColor, + 'stroke-width': borderWidth + }); + } + + // bottom + elements.bottom.attr({ + fill: toRaphaelColor(colorObj.bottom) + }); + + // bottom + elements.bottomBorder.attr({ + stroke: borderColor, + 'stroke-width': borderWidth + }); + + elements.frontOuter.attr({ + fill: toRaphaelColor(colorObj.frontOuter) + }); + elements.frontOuter1.attr({ + fill: toRaphaelColor(colorObj.frontOuter) + }); + + // outerback + elements.backOuter.attr({ + fill: toRaphaelColor(colorObj.backOuter) + }); + + // startSlice + // whenAtBack + elements.startSlice.attr({ + fill: toRaphaelColor(colorObj.startSlice), + stroke: borderColor, + 'stroke-width': borderWidth + }); + + // endSlice + // whenAtBack + elements.endSlice.attr({ + fill: toRaphaelColor(colorObj.endSlice), + stroke: borderColor, + 'stroke-width': borderWidth + }); + if (manager.isDoughnut) { + // innerFront + elements.frontInner.attr({ + fill: toRaphaelColor(colorObj.frontInner) + }); + + elements.backInner.attr({ + fill: toRaphaelColor(colorObj.backInner) + }); + } + + }, + + /** + * This function create a new 3d slice and return that for futher use + * @returns {slice} an raphael like composite object that can be used as a slice element + * @todo update slice color depending upon angle + * @todo For proper z-index create 2 back outer element for larger (>180 deg) slices + * @todo Optimize ***** the element creation, instead of outer1, back and from for all elements, + * create few extra element per manager, which will act as outer1, back / front (inner / outer wall) for + * slices, who has both. Because, at max 1 slice can have outer1 and only 2 slice can have + * back and front both + */ + createSlice: (function () { + var attrKeyList = { + //block following attribute + stroke: true, + strokeWidth: true, + 'stroke-width': true, + dashstyle: true, + 'stroke-dasharray': true, + translateX: true, + translateY: true, + 'stroke-opacity': true, + fill: true, + opacity: true, + // attribute that has direct effect + transform: true, + ishot : true, + cursor: true, + start: true, + end : true, + color: true, + alpha: true, + borderColor: true, + borderAlpha: true, + borderWidth: true, + rolloverProps: true, + showBorderEffect: true, + positionIndex: true, + cx: true, + cy: true, + radiusYFactor: true, + r: true, + innerR: true + }, + attrFN = function(hash, val) { + var key, + value, + slice = this, + confObject = slice._confObject, + commonAttr = {}, + elements = confObject.elements, + x, + updateShape, + updateColor, + Pie3DManager = confObject.Pie3DManager, + applyCommonAttr, + attrObj; + + // single key-value pair + if (isString(hash) && defined(val)) { + key = hash; + hash = {}; + hash[key] = val; + } + + // used as a getter: first argument is a string, second is undefined + if (!hash || isString(hash)) { + if (attrKeyList[hash]) { + slice = confObject[hash]; + } + else { + slice = slice._attr(hash); + } + // setter + } else { + + for (key in hash) { + value = hash[key]; + + //if belongs from the list then handle here + if (attrKeyList[key]) { + //store the att in confObject for further use + confObject[key] = value; + if (key === 'ishot' || key === 'cursor' || key === 'transform') { + attrObj = {}; + commonAttr[key] = value; + applyCommonAttr = true; + + } + /* jshint ignore:end */ + else if (key === 'start' || key === 'end' || key === 'cx' || key === 'cy' || + key === 'radiusYFactor' || key === 'r' || key === 'innerR') { + updateShape = true; + } + else if (key === 'color' || key === 'alpha' || key === 'borderColor' || + key === 'borderAlpha' || key === 'borderWidth'){ + updateColor = true; + } + } else { //else leve for the original attr + slice._attr(key, value); + } + } + + // if paths need to be updated + if (updateShape) { + Pie3DManager._setSliceShape(confObject); + //refreash the drawinh for proper z lavel for elements + Pie3DManager.refreshDrawing(); + } + // if colors need to be updated + // If the shape got changed, then also cange the color + if (updateColor || updateShape) { + Pie3DManager._setSliceCosmetics(confObject); + } + // apply common attributes + if (applyCommonAttr) { + //other elements + for (x in elements) { + elements[x].attr(commonAttr); + } + //main element + slice._attr(commonAttr); + } + + } + return slice; + }, + onFN = function (eventType, handler) { + var slice = this, + confObject = slice._confObject, + elements = confObject.elements, + element; + + for (element in elements) { + elements[element].on(eventType, handler); + } + return slice._on(eventType, handler); + }, + onDragFN = function (dragStart, dragMove, dragEnd) { + var element, + slice = this, + confObject = slice._confObject, + elements = confObject.elements, + navigator = win.navigator, + ua = navigator.userAgent.toLowerCase(), + isAndroid = ua.indexOf('android') > -1; + for (element in elements) { + if (isAndroid) { + if (element === 'topBorder' || element === 'frontOuter' || element === 'startSlice' || + element === 'endSlice') { + elements[element].drag(dragStart, dragMove, dragEnd); + } + } + else { + elements[element].drag(dragStart, dragMove, dragEnd); + } + } + return slice._drag(dragStart, dragMove, dragEnd); + }, + hideFN = function () { + var slice = this, + confObject = slice._confObject, + elements = confObject.elements, + element; + + for (element in elements) { + elements[element].hide(); + } + return slice._hide(); + }, + showFN = function () { + var slice = this, + confObject = slice._confObject, + elements = confObject.elements, + element; + + for (element in elements) { + elements[element].show(); + } + return slice._show(); + }, + destroyFN = function () { + var confObject = this._confObject, + elements = confObject.elements, x; + //other elements + for (x in elements) { + elements[x].destroy(); + } + if (hasSVG) { + //destory other element + /** @todo check whether this clip elements are not destroying from else where */ + confObject.clipTop.destroy(); + confObject.clipOuterFront.destroy(); + confObject.clipOuterBack.destroy(); + if (confObject.clipOuterFront1) { + confObject.clipOuterFront1.destroy(); + } + if (confObject.clipInnerFront) { + confObject.clipInnerFront.destroy(); + } + if (confObject.clipInnerBack) { + confObject.clipInnerBack.destroy(); + } + } + //main element + return this._destroy(); + }, + tooltipFN = function (tooltext) { + var slice = this, + confObject = slice._confObject, + elements = confObject.elements, + element; + //other elements + for (element in elements) { + elements[element].tooltip(tooltext); + } + //main element + return slice._tooltip(tooltext); + }, + dataFN = function (key, value) { + var slice = this, + confObject = slice._confObject, + elements = confObject.elements, + element; + if (value === undefined) { + return slice._data(key); + } + else { + //other elements + for (element in elements) { + elements[element].data(key, value); + } + //main element + return slice._data(key, value); + } + }, + si = 0; // slice index + return function () { + + var manager = this, + renderer = manager.renderer, + returnElement, + confObject = { + elements : {}, + Pie3DManager : manager + }, + slicingWallsArr = manager.slicingWallsArr, + elements = confObject.elements, + renderingPath = hasSVG ? 'litepath' : 'path'; + + // create elements + returnElement = renderer[renderingPath](manager.topGroup); + //store the _confObject reference + returnElement._confObject = confObject; + confObject.thisElement = returnElement; + + //modify few core function + + returnElement._destroy = returnElement.destroy; + returnElement.destroy = destroyFN; + + returnElement._show = returnElement.show; + returnElement.show = showFN; + + returnElement._hide = returnElement.hide; + returnElement.hide = hideFN; + + returnElement._on = returnElement.on; + returnElement.on = onFN; + + returnElement._drag = returnElement.drag; + returnElement.drag = onDragFN; + + returnElement._attr = returnElement.attr; + returnElement.attr = attrFN; + + returnElement._tooltip = returnElement.tooltip; + returnElement.tooltip = tooltipFN; + + returnElement._data = returnElement.data; + returnElement.data = dataFN; + + //add the element to the store + manager.pointElemStore.push(returnElement); + + elements.topBorder = renderer[renderingPath](manager.topGroup); + elements.bottom = renderer[renderingPath](manager.bottomBorderGroup) + .attr({ + 'stroke-width' : 0 + }); + elements.bottomBorder = renderer[renderingPath](manager.bottomBorderGroup); + elements.frontOuter = renderer[renderingPath](manager.slicingWallsFrontGroup) + .attr({ + 'stroke-width' : 0 + }); + elements.backOuter = renderer[renderingPath](manager.slicingWallsFrontGroup) + .attr({ + 'stroke-width' : 0 + }); + elements.startSlice = renderer[renderingPath](manager.slicingWallsFrontGroup); + elements.endSlice = renderer[renderingPath](manager.slicingWallsFrontGroup); + elements.frontOuter1 = renderer[renderingPath](manager.slicingWallsFrontGroup) + .attr({ + 'stroke-width' : 0 + }); + + // update config object for proper z order + elements.frontOuter._conf = { + si: si, + isStart : 0.5 + }; + elements.frontOuter1._conf = { + si: si, + isStart : 0.5 + }; + elements.startSlice._conf = { + si: si, + isStart : 0 + }; + elements.endSlice._conf = { + si: si, + isStart : 1 + }; + elements.backOuter._conf = { + si: si, + isStart : 0.4 + }; + slicingWallsArr.push(elements.startSlice, elements.frontOuter1, elements.frontOuter, + elements.backOuter, elements.endSlice); + if (manager.isDoughnut) { + // innerFront + elements.frontInner = renderer[renderingPath](manager.slicingWallsFrontGroup) + .attr({ + 'stroke-width' : 0 + }); + elements.backInner = renderer[renderingPath](manager.slicingWallsFrontGroup) + .attr({ + 'stroke-width' : 0 + }); + + elements.backInner._conf = { + si: si, + isStart : 0.5 + }; + + elements.frontInner._conf = { + si: si, + isStart : 0.4 + }; + slicingWallsArr.push(elements.frontInner, elements.backInner); + } + + si += 1; + return returnElement; + }; + })() + }; + + Pie3DManager.prototype.constructor = Pie3DManager; + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-doughnut2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI, + getFirstColor = lib.getFirstColor, + getFirstAlpha = lib.getFirstAlpha, + hasSVG = lib.hasSVG, + math = Math, + docMode8 = win.document.documentMode === 8, + toRaphaelColor = lib.toRaphaelColor, + hashify = lib.hashify, + HIDDEN = 'hidden', + VISIBLE = docMode8 ? 'visible' : '', + getLightColor = lib.graphics.getLightColor, + mathFloor = math.floor, + COMMA = ',', + getDarkColor = lib.graphics.getDarkColor; + + chartAPI ('doughnut2d', { + friendlyName: 'Doughnut Chart', + defaultDatasetType : 'Doughnut2D', + creditLabel: creditLabel, + applicableDSList: { + 'Doughnut2D': true + }, + getPointColor: function (color, alpha, radius3D) { + var colorObj, + loLight, + hiLight; + + color = getFirstColor (color); + alpha = getFirstAlpha (alpha); + + // Radial gradient is not supported in VML, hence we use for SVG + // alone. + if (radius3D < 100 && hasSVG) { + loLight = getDarkColor (color, + mathFloor ((85 - 0.2 * (100 - radius3D)) * 100) / 100); + + hiLight = getLightColor (color, + mathFloor ((100 - 0.5 * radius3D) * 100) / 100); + + colorObj = { + FCcolor: { + color: loLight + COMMA + hiLight + COMMA + hiLight + + COMMA + loLight, + alpha: alpha + COMMA + alpha + COMMA + alpha + COMMA + alpha, + radialGradient: true, + gradientUnits: 'userSpaceOnUse', + r: radius3D + } + }; + } + else { + /** @todo replace the single shade radial to solid fill */ + colorObj = { + FCcolor: { + color: color + COMMA + color, + alpha: alpha + COMMA + alpha, + ratio: '0,100' + } + }; + } + + return colorObj; + }, + drawDoughnutCenterLabel: function (labelText, cx, cy, dx, dy, centerLabelConfig, updateConfig) { + var chart = this, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + seriesData = dataSet.config, + labelConfig = centerLabelConfig || seriesData.lastCenterLabelConfig, + paper = chartComponents.paper, + smartLabel = chart.linkedItems.smartLabel, + chartGraphics = chart.graphics, + grp = chartGraphics.datasetGroup, + labelPadding = labelConfig.padding, + textpadding = labelConfig.textPadding * 2, + cssObj = { + fontFamily: labelConfig.font, + fontSize: labelConfig.fontSize + 'px', + lineHeight: (1.2 * labelConfig.fontSize) + 'px', + fontWeight: labelConfig.bold ? 'bold' : '', + fontStyle: labelConfig.italic ? 'italic' : '' + }, + txtW = ((dx * 0.5 - labelPadding) * 1.414) - textpadding, + txtH = ((dy * 0.5 - labelPadding) * 1.414) - textpadding, + centerLabel, + smartLabelObj, + labelOvalBg; + + smartLabel.setStyle(cssObj); + smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow); + smartLabelObj = smartLabel.getSmartText(labelText, txtW, txtH); + + if (!(centerLabel = chartGraphics.doughnutCenterLabel)) { + labelConfig.bgOval && + (chartGraphics.centerLabelOvalBg = labelOvalBg = paper.circle(cx, cy, dx * 0.5 - labelPadding, + grp)); + centerLabel = chartGraphics.doughnutCenterLabel = paper.text(grp) + .hover(chart.centerLabelRollover, chart.centerLabelRollout) + .click(chart.centerLabelClick); + /** @todo: Fix reference issue */ + centerLabel.chart = chart; + } + else { + centerLabel.attr('text') !== labelText && chart.centerLabelChange(labelText); + labelOvalBg = chartGraphics.centerLabelOvalBg; + } + + if (labelText) { + centerLabel.css(cssObj) + .attr({ + x: cx, + y: cy, + text: smartLabelObj.text, + visibility: VISIBLE, + direction: seriesData.textDirection, + //title: labelConfig.toolText ? '' : smartLabelObj.tooltext || '', + fill: toRaphaelColor({ + FCcolor: { + color: labelConfig.color, + alpha: labelConfig.alpha + } + }), + 'text-bound': labelConfig.bgOval ? 'none' : [ + toRaphaelColor({ + FCcolor: { + color: labelConfig.bgColor, + alpha: labelConfig.bgAlpha + } + }), + toRaphaelColor({ + FCcolor: { + color: labelConfig.borderColor, + alpha: labelConfig.borderAlpha + } + }), + labelConfig.borderThickness, + labelConfig.textPadding, + labelConfig.borderRadius + ] + }) + .tooltip(labelConfig.toolText || smartLabelObj.tooltext); + + if (labelConfig.bgOval) { + labelOvalBg && labelOvalBg.attr({ + visibility: VISIBLE, + fill: hashify(labelConfig.bgColor), + 'fill-opacity': labelConfig.bgAlpha / 100, + stroke: hashify(labelConfig.borderColor), + 'stroke-width': labelConfig.borderThickness, + 'stroke-opacity': labelConfig.borderAlpha / 100 + }); + } + } + else { + centerLabel.attr('visibility', HIDDEN); + labelOvalBg && labelOvalBg.attr('visibility', HIDDEN); + } + + if (updateConfig) { + seriesData.lastCenterLabelConfig = labelConfig; + seriesData.centerLabelConfig = labelConfig; + } + }, + + centerLabelRollover: function () { + var chart = this.chart, + chartComponents = chart.components, + chartConfig = chart.config, + dataSet = chartComponents.dataset[0], + chartInstance = chart.chartInstance, + seriesData = dataSet.config, + ref = chartInstance.ref, + labelConfig = seriesData.lastCenterLabelConfig, + eventArgs = { + height: chartConfig.height, + width: chartConfig.width, + pixelHeight: ref.offsetHeight, + pixelWidth: ref.offsetWidth, + id: chartInstance.id, + renderer: chartInstance.args.renderer, + container: chart.linkedItems.container, + centerLabelText: labelConfig && labelConfig.label + }; + /** + * This event is fired on mouse rollover on label at center of doughnut 2D. + * + * > Available on `doughnut` chart only. + * + * @group chart:pie-center-label + * @event FusionCharts#centerLabelRollover + * + * @param {string} centerLabelText - is the text for display at center label + * @param {number} chartX - is the relative X-Cordinate to chart container where the chart was clicked + * @param {number} chartY - is the relative Y-Cordinate to chart container where the chart was clicked. + * @param {string} container - is the DOM element where the chart is being rendered. + * @param {numeric|percent} height - height of the chart + * @param {numeric|percent} width - width of the chart + * @param {string} id - is the chart id + * @param {number} pageX - is the relative X-Cordinate to screen where the chart is clicked + * @param {number} pageY - is the relative Y-Cordinate to screen where the chart is clicked + * @param {number} pixelHeight - is the height of the DOM element where the chart is being rendered in + * pixels + * @param {number} pixelWidth - is the width of the DOM element where the chart is being rendered in + * pixels + * @param {string} renderer - tells if the chart is rendered using JavaScript or Flash + */ + this.attr('text') && global.raiseEvent('centerLabelRollover', + eventArgs, chartInstance, this, chart.hoverOnCenterLabel); + }, + + centerLabelRollout: function () { + var chart = this.chart, + chartConfig = chart.config, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + chartInstance = chart.chartInstance, + ref = chartInstance.ref, + seriesData = dataSet.config, + labelConfig = seriesData.lastCenterLabelConfig, + eventArgs = { + height: chartConfig.height, + width: chartConfig.width, + pixelHeight: ref.offsetHeight, + pixelWidth: ref.offsetWidth, + id: chartInstance.id, + renderer: chartInstance.args.renderer, + container: chart.linkedItems.container, + centerLabelText: labelConfig && labelConfig.label + }; + /** + * This event is fired on mouse rollout from label at center of + * doughnut 2D. + * + * > Available on `doughnut` chart only. + * + * @group chart:pie-center-label + * @event FusionCharts#centerLabelRollout + * + * @param {string} centerLabelText - is the text for display at center label + * @param {number} chartX - is the relative X-Cordinate to chart container where the chart was clicked + * @param {number} chartY - is the relative Y-Cordinate to chart container where the chart was clicked. + * @param {string} container - is the DOM element where the chart is being rendered. + * @param {numeric|percent} height - height of the chart + * @param {numeric|percent} width - width of the chart + * @param {string} id - is the chart id + * @param {number} pageX - is the relative X-Cordinate to screen where the chart is clicked + * @param {number} pageY - is the relative Y-Cordinate to screen where the chart is clicked + * @param {number} pixelHeight - is the height of the DOM element where the chart is being rendered in + * pixels + * @param {number} pixelWidth - is the width of the DOM element where the chart is being rendered in + * pixels + * @param {string} renderer - tells if the chart is rendered using JavaScript or Flash + */ + this.attr('text') && global.raiseEvent('centerLabelRollout', eventArgs, chartInstance, this, + chart.hoverOffCenterLabel); + }, + + centerLabelClick: function () { + var chart = this.chart, + chartConfig = chart.config, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + chartInstance = chart.chartInstance, + ref = chartInstance.ref, + seriesData = dataSet.config, + labelConfig = seriesData.lastCenterLabelConfig, + eventArgs = { + height: chartConfig.height, + width: chartConfig.width, + pixelHeight: ref.offsetHeight, + pixelWidth: ref.offsetWidth, + id: chartInstance.id, + renderer: chartInstance.args.renderer, + container: chart.linkedItems.container, + centerLabelText: labelConfig && labelConfig.label + }; + /** + * This event is fired on click on label at center of doughnut 2D. + * + * > Available on `doughnut` chart only. + * + * @group chart:pie-center-label + * @event FusionCharts#centerLabelClick + * + * @param {string} centerLabelText - is the text for display at center label. + * @param {number} chartX - is the relative X-Cordinate to chart container where the chart was clicked. + * @param {number} chartY - is the relative Y-Cordinate to chart container where the chart was clicked. + * @param {string} container - is the DOM element where the chart is being rendered. + * @param {numeric|percent} height - height of the chart + * @param {numeric|percent} width - width of the chart + * @param {string} id - is the chart id + * @param {number} pageX - is the relative X-Cordinate to screen where the chart is clicked + * @param {number} pageY - is the relative Y-Cordinate to screen where the chart is clicked + * @param {number} pixelHeight - is the height of the DOM element where the chart is being rendered in + * pixels + * @param {number} pixelWidth - is the width of the DOM element where the chart is being rendered in + * pixels + * @param {string} renderer - tells if the chart is rendered using JavaScript or Flash + */ + this.attr('text') && global.raiseEvent('centerLabelClick', eventArgs, chartInstance); + }, + + centerLabelChange: function (labelText) { + var chart = this, + chartConfig = chart.config, + chartInstance = chart.chartInstance, + ref = chartInstance.ref, + eventArgs = { + height: chartConfig.height, + width: chartConfig.width, + pixelHeight: ref.offsetHeight, + pixelWidth: ref.offsetWidth, + id: chartInstance.id, + renderer: chartInstance.args.renderer, + container: chart.linkedItems.container, + centerLabelText: labelText + }; + /** + * This event is fired on change of label at center of doughnut 2D. + * + * > Available on `doughnut` chart only. + * + * @group chart:pie-center-label + * @event FusionCharts#centerLabelChanged + * + * @param {string} centerLabelText - is the text for display at center label + * @param {number} chartX - is the relative X-Cordinate to chart container where the chart was clicked + * @param {number} chartY - is the relative Y-Cordinate to chart container where the chart was clicked. + * @param {string} container - is the DOM element where the chart is being rendered. + * @param {numeric|percent} height - height of the chart + * @param {numeric|percent} width - width of the chart + * @param {string} id - is the chart id + * @param {number} pageX - is the relative X-Cordinate to screen where the chart is clicked + * @param {number} pageY - is the relative Y-Cordinate to screen where the chart is clicked + * @param {number} pixelHeight - is the height of the DOM element where the chart is being rendered in + * pixels + * @param {number} pixelWidth - is the width of the DOM element where the chart is being rendered in + * pixels + * @param {string} renderer - tells if the chart is rendered using JavaScript or Flash + */ + global.raiseEvent('centerLabelChanged', eventArgs, chartInstance); + }, + + hoverOnCenterLabel: function () { + var chart = this.chart, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + seriesData = dataSet.config, + labelConfig = seriesData.lastCenterLabelConfig; + + if (labelConfig.hoverColor || labelConfig.hoverAlpha) { + this.attr({fill: toRaphaelColor({ + FCcolor: { + color: labelConfig.hoverColor || labelConfig.color, + alpha: labelConfig.hoverAlpha || labelConfig.alpha + } + }) + }); + } + }, + + hoverOffCenterLabel: function () { + var chart = this.chart, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + seriesData = dataSet.config, + labelConfig = seriesData.lastCenterLabelConfig; + + if (labelConfig.hoverColor || labelConfig.hoverAlpha) { + this.attr({fill: toRaphaelColor({ + FCcolor: { + color: labelConfig.color, + alpha: labelConfig.alpha + } + }) + }); + } + } + }, chartAPI.pie2d, { + singletonPlaceValue: false + }); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-doughnut3d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('doughnut3d', { + friendlyName: '3D Doughnut Chart', + defaultDatasetType : 'Doughnut3D', + creditLabel: creditLabel, + applicableDSList: { + 'Doughnut3D': true + }, + _configureManager: function () { + var chart = this, + chartComponents = chart.components, + dataSet = chartComponents.dataset[0], + dataSetConfig = dataSet.config, + dataSetComponents = dataSet.components, + pie3DManager = dataSetComponents.Pie3DManager, + data = dataSetComponents.data; + + if (pie3DManager) { + pie3DManager.configure(dataSetConfig.pieSliceDepth, data.length === 1, dataSetConfig.use3DLighting, + true); + } + } + }, chartAPI.pie3d); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-mscolumn2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('mscolumn2d', { + standaloneInit: true, + friendlyName: 'Multi-series Column Chart', + creditLabel: creditLabel, + defaultDatasetType : 'column', + applicableDSList: { 'column': true }, + eiMethods : { } + },chartAPI.mscartesian, { + enablemousetracking: true + }); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-mscolumn3d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('mscolumn3d', { + standaloneInit: true, + defaultDatasetType: 'column3d', + friendlyName: 'Multi-series 3D Column Chart', + applicableDSList: { 'column3d': true }, + // Default shadow is visible for 3D variant of MSColumn2D chart + defaultPlotShadow: 1, + fireGroupEvent: true, + is3D: true, + creditLabel: creditLabel, + defaultZeroPlaneHighlighted: false + }, chartAPI.mscartesian3d, { + showplotborder: 0, + enablemousetracking: true + }); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-msbar2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname); + + chartAPI ('msbar2d', { + standaloneInit: true, + friendlyName: 'Multi-series Bar Chart', + isBar: true, + hasLegend: true, + creditLabel: creditLabel, + defaultDatasetType: 'bar2d', + applicableDSList: { 'bar2d': true } + }, chartAPI.msbarcartesian, { + enablemousetracking: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-msbar3d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname); + + chartAPI ('msbar3d', { + standaloneInit: true, + defaultSeriesType: 'bar3d', + friendlyName: 'Multi-series 3D Bar Chart', + fireGroupEvent: true, + defaultPlotShadow: 1, + is3D: true, + isBar: true, + hasLegend: true, + creditLabel: creditLabel, + defaultZeroPlaneHighlighted: false, + defaultDatasetType: 'bar3d', + applicableDSList: { 'bar3d': true } + }, chartAPI.msbarcartesian3d, { + showplotborder: 0, + enablemousetracking: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-msarea', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('msarea', { + standaloneInit: true, + friendlyName: 'Multi-series Area Chart', + creditLabel: creditLabel, + defaultDatasetType : 'area', + defaultPlotShadow: 0, + applicableDSList: { 'area': true } + }, chartAPI.areabase, { + enablemousetracking: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-msline', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname); + + chartAPI ('msline', { + standaloneInit: true, + friendlyName: 'Multi-series Line Chart', + creditLabel: creditLabel, + defaultDatasetType : 'line', + defaultPlotShadow: 1, + axisPaddingLeft: 0, + axisPaddingRight: 0, + applicableDSList: { 'line': true } + }, chartAPI.areabase, { + zeroplanethickness: 1, + zeroplanealpha: 40, + showzeroplaneontop: 0, + enablemousetracking: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-stackedarea2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + preDefStr = lib.preDefStr, + HUNDREDSTRING = preDefStr.HUNDREDSTRING; + + chartAPI ('stackedarea2d', { + friendlyName: 'Stacked Area Chart', + showsum: 0, + creditLabel: creditLabel + }, chartAPI.msarea, { + plotfillalpha: HUNDREDSTRING, + isstacked: 1 + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-stackedcolumn2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname); + + chartAPI ('stackedcolumn2d', { + friendlyName: 'Stacked Column Chart', + creditLabel: creditLabel + }, chartAPI.mscolumn2d, { + isstacked: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-stackedcolumn3d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('stackedcolumn3d', { + friendlyName: '3D Stacked Column Chart', + creditLabel: creditLabel, + _mouseEvtHandler: function (e) { + var iapi = this, + data = e.data, + mouseTracker = data.mouseTracker, + oriEvent = e.originalEvent, + chartConfig = iapi.config, + canvasLeft = chartConfig.canvasLeft, + canvasRight = chartConfig.canvasRight, + canvasBottom = chartConfig.canvasBottom, + canvasTop = chartConfig.canvasTop, + datasets = chartConfig.datasetOrder || iapi.components.dataset, + coordinate = lib.getMouseCoordinate(iapi.linkedItems.container, oriEvent, iapi), + chartX = coordinate.chartX, + chartY = coordinate.chartY, + dataset, + chartComponents = iapi.components, + group = 'datasetGroup_' + datasets[0].type, + hoveredInfo, + pointFound = false, + i = datasets.length, + j, + l, + derivedEvensInfo, + _lastDatasetIndex = mouseTracker._lastDatasetIndex, + _lastPointIndex = mouseTracker._lastPointIndex, + MOUSEOUT = 'mouseout'; + + // todo we have to implement this for charts with more than one canvas like candle stick + // if inside the canvas + if (chartX > canvasLeft && chartX < canvasRight && chartY > canvasTop && chartY < canvasBottom || + iapi.config.plotOverFlow) { + // todo make sure the datasets are as per their z-order + while (i-- && !pointFound) { + dataset = datasets[i]; + dataset.valueLook = true; + if (dataset && dataset.visible) { + hoveredInfo = dataset._getHoveredPlot && dataset._getHoveredPlot(chartX, chartY); + if (hoveredInfo && hoveredInfo.hovered) { + pointFound = true; + hoveredInfo.datasetIndex = i; + derivedEvensInfo = mouseTracker._getMouseEvents(e, hoveredInfo.datasetIndex, + hoveredInfo.pointIndex); + } + } + } + + i = 0; + while (i < datasets.length) { + if (pointFound) { + break; + } + dataset = datasets[i]; + dataset.valueLook = false; + if (dataset && dataset.visible) { + hoveredInfo = dataset._getHoveredPlot && dataset._getHoveredPlot(chartX, chartY); + if (hoveredInfo && hoveredInfo.hovered) { + pointFound = true; + hoveredInfo.datasetIndex = i; + derivedEvensInfo = mouseTracker._getMouseEvents(e, hoveredInfo.datasetIndex, + hoveredInfo.pointIndex); + } + } + i++; + } + } + + // todo instead of sending event names, create a event object of that type and send it + + // fire out on last hovered plot + if ((!pointFound || (derivedEvensInfo && derivedEvensInfo.fireOut)) && + _lastDatasetIndex !== undefined) { + // delete stored last ds details + delete mouseTracker._lastDatasetIndex; + delete mouseTracker._lastPointIndex; + + datasets[_lastDatasetIndex] && datasets[_lastDatasetIndex]._firePlotEvent && + datasets[_lastDatasetIndex]._firePlotEvent(MOUSEOUT, _lastPointIndex, e); + + // todo scope to have sticky tracked tooltip + } + // fire remaining events + if (pointFound) { + l = derivedEvensInfo.events && derivedEvensInfo.events.length; + // store the index of the hovered DS and plot + mouseTracker._lastDatasetIndex = hoveredInfo.datasetIndex; + _lastPointIndex = mouseTracker._lastPointIndex = hoveredInfo.pointIndex; + + chartConfig.drawTrendRegion && chartComponents[group]._notifyGroup(true, e); + for (j = 0; j < l; j += 1) { + dataset && dataset._firePlotEvent && dataset._firePlotEvent(derivedEvensInfo.events[j], + _lastPointIndex, e, hoveredInfo.datasetIndex); + } + } + else { + chartConfig.drawTrendRegion && chartComponents[group]._notifyGroup(false, e); + } + + chartConfig.drawTrendRegion && chartComponents[group]._getHoveredRegion(chartX, chartY, e); + } + }, chartAPI.mscolumn3d, { + showplotborder: 0, + enablemousetracking: true + }, chartAPI.stackedcolumn2d); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-stackedbar2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('stackedbar2d', { + friendlyName: 'Stacked Bar Chart', + creditLabel: creditLabel + }, chartAPI.msbar2d, { + maxbarheight: 50, + enablemousetracking: true + }, chartAPI.stackedcolumn2d); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-stackedbar3d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('stackedbar3d', { + friendlyName: '3D Stacked Bar Chart', + creditLabel: creditLabel, + _mouseEvtHandler: function (e) { + var iapi = this, + data = e.data, + mouseTracker = data.mouseTracker, + oriEvent = e.originalEvent, + chartConfig = iapi.config, + canvasLeft = chartConfig.canvasLeft, + canvasRight = chartConfig.canvasRight, + canvasBottom = chartConfig.canvasBottom, + canvasTop = chartConfig.canvasTop, + datasets = chartConfig.datasetOrder || iapi.components.dataset, + coordinate = lib.getMouseCoordinate(iapi.linkedItems.container, oriEvent, iapi), + chartX = coordinate.chartX, + chartY = coordinate.chartY, + dataset, + chartComponents = iapi.components, + group = 'datasetGroup_' + datasets[0].type, + hoveredInfo, + pointFound = false, + i = datasets.length, + k, + j, + l, + derivedEvensInfo, + _lastDatasetIndex = mouseTracker._lastDatasetIndex, + _lastPointIndex = mouseTracker._lastPointIndex, + MOUSEOUT = 'mouseout', + datasetBuffer, + hoveredInfoBuffer; + + // todo we have to implement this for charts with more than one canvas like candle stick + // if inside the canvas + if (chartX > canvasLeft && chartX < canvasRight && chartY > canvasTop && chartY < canvasBottom || + iapi.config.plotOverFlow) { + // todo make sure the datasets are as per their z-order + i = datasets.length; + while (i-- && !pointFound) { + dataset = datasets[i]; + dataset.valueLook = true; + if (dataset && dataset.visible) { + hoveredInfo = dataset._getHoveredPlot && dataset._getHoveredPlot(chartX, chartY); + + if (hoveredInfo && hoveredInfo.hovered) { + for (k = 0; k <= i; ++k) { + if (i === k || !hoveredInfo) { + continue; + } + datasetBuffer = datasets[k]; + hoveredInfoBuffer = datasetBuffer._getHoveredPlot && + datasetBuffer._getHoveredPlot(chartX, chartY); + if (hoveredInfoBuffer && + hoveredInfoBuffer.pointIndex === hoveredInfo.pointIndex - 1) { + hoveredInfo = hoveredInfoBuffer; + i = k; + dataset = datasetBuffer; + dataset.valueLook = true; + break; + } + } + } + + + if (hoveredInfo && hoveredInfo.hovered) { + pointFound = true; + hoveredInfo.datasetIndex = i; + derivedEvensInfo = mouseTracker._getMouseEvents(e, hoveredInfo.datasetIndex, + hoveredInfo.pointIndex); + } + } + } + + i = 0; + while (i < datasets.length) { + if (pointFound) { + break; + } + dataset = datasets[i]; + dataset.valueLook = false; + if (dataset && dataset.visible) { + hoveredInfo = dataset._getHoveredPlot && dataset._getHoveredPlot(chartX, chartY); + + if (hoveredInfo && hoveredInfo.hovered) { + for (k = 0; k <= i; ++k) { + if (i === k || !hoveredInfo) { + continue; + } + datasetBuffer = datasets[k]; + hoveredInfoBuffer = datasetBuffer._getHoveredPlot && + datasetBuffer._getHoveredPlot(chartX, chartY); + if (hoveredInfoBuffer && + hoveredInfoBuffer.pointIndex === hoveredInfo.pointIndex - 1) { + hoveredInfo = hoveredInfoBuffer; + i = k; + dataset = datasetBuffer; + dataset.valueLook = false; + break; + } + + } + } + + if (hoveredInfo && hoveredInfo.hovered) { + pointFound = true; + hoveredInfo.datasetIndex = i; + derivedEvensInfo = mouseTracker._getMouseEvents(e, hoveredInfo.datasetIndex, + hoveredInfo.pointIndex); + } + } + i++; + } + } + + // todo instead of sending event names, create a event object of that type and send it + + // fire out on last hovered plot + if ((!pointFound || (derivedEvensInfo && derivedEvensInfo.fireOut)) && + _lastDatasetIndex !== undefined) { + // delete stored last ds details + delete mouseTracker._lastDatasetIndex; + delete mouseTracker._lastPointIndex; + datasets[_lastDatasetIndex] && datasets[_lastDatasetIndex]._firePlotEvent && + datasets[_lastDatasetIndex]._firePlotEvent(MOUSEOUT, _lastPointIndex, e); + + // todo scope to have sticky tracked tooltip + } + // fire remaining events + if (pointFound) { + l = derivedEvensInfo.events && derivedEvensInfo.events.length; + // store the index of the hovered DS and plot + mouseTracker._lastDatasetIndex = hoveredInfo.datasetIndex; + _lastPointIndex = mouseTracker._lastPointIndex = hoveredInfo.pointIndex; + + chartConfig.drawTrendRegion && chartComponents[group]._notifyGroup(true, e); + for (j = 0; j < l; j += 1) { + + dataset && dataset._firePlotEvent && dataset._firePlotEvent(derivedEvensInfo.events[j], + _lastPointIndex, e, hoveredInfo.datasetIndex); + } + } + else { + chartConfig.drawTrendRegion && chartComponents[group]._notifyGroup(false, e); + } + + chartConfig.drawTrendRegion && chartComponents[group]._getHoveredRegion(chartX, chartY, e); + } + /*, + _mouseEvtHandler: function (e) { + chartAPI.stackedcolumn3d._mouseEvtHandler.call(this, e); + }*/ + }, chartAPI.msbar3d, { + showplotborder: 0, + enablemousetracking: true + }, chartAPI.stackedcolumn2d); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-marimekko', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + math = Math, + mathMin = math.min, + mathMax = math.max; + + chartAPI ('marimekko', { + standaloneInit: true, + friendlyName: 'Marimekko Chart', + isValueAbs: true, + distributedColumns: true, + stack100percent: true, + defaultDatasetType: 'marimekko', + applicableDSList: {'marimekko': true}, + isStacked: true, + showsum: 1, + creditLabel: creditLabel, + _setAxisLimits : function () { + var iapi = this, + components = iapi.components, + dataset = components.dataset, + yAxis = components.yAxis, + xAxis = components.xAxis, + currentDataset, + length = dataset.length, + i, + infMin = -Infinity, + infMax = +Infinity, + max = infMin, + min = infMax, + xMin = infMax, + xMax = infMin, + lastXPos, + maxminObj, + groupManager, + xLimit, + xRange, + len, + catLen, + value, + catXVal, + groupManagerObj = { }, + categories = iapi.config.categories, + stackPercentValue, + stackValWidth, + noManager = [], + getMaxMin = function (maxminObj) { + max = mathMax (max, maxminObj.max); + min = mathMin (min, maxminObj.min); + xMax = mathMax (xMax, maxminObj.xMax || infMin); + xMin = mathMin (xMin, maxminObj.xMin || infMax); + + }; + + for (i=0; i len) { + categories.splice(len, catLen - len); + } + iapi._setCategories(); + xLimit = xAxis[0].getLimit(); + xMin = xLimit.min; + xMax = xLimit.max; + lastXPos = xMin; + + xRange = xMax - xMin; + for (i = 0; i < stackPercentValue.length; i++) { + value = stackPercentValue[i]; + stackValWidth = xRange * value/100; + catXVal = lastXPos + (stackValWidth / 2); + xAxis[0].updateCategory(i, {x: catXVal}); + lastXPos += stackValWidth; + } + } + }, chartAPI.mscartesian, { + isstacked: true, + showpercentvalues: 0, + usepercentdistribution: 1, + showsum: 1, + enablemousetracking: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-msstackedcolumn2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI, + pluck = lib.pluck, + componentDispose = lib.componentDispose; + + chartAPI ('msstackedcolumn2d', { + standaloneInit: true, + defaultDatasetType: 'column', + applicableDSList: { 'column': true }, + friendlyName: 'Multi-series Stacked Column Chart', + _createDatasets: function () { + var iapi = this, + components = iapi.components, + dataObj = iapi.jsonData, + dataset = dataObj.dataset, + length = (dataset && dataset.length) || 0, + i, + j, + datasetStore, + datasetObj, + defaultSeriesType = iapi.defaultDatasetType, + applicableDSList = iapi.applicableDSList, + GroupManager, + dsType, + DsClass, + DsGroupClass, + datasetJSON, + groupManagerName, + parentDataObj, + innerLength, + lineSets = dataObj.lineset || [], + lineSetJSON, + config = iapi.config, + dataSetMap = config.dataSetMap, + lineSetMap = config.lineSetMap, + dataSetMapLen = dataSetMap && dataSetMap.length, + legend = components.legend, + tempDataStoreMap = [], + tempLineStoreMap = [], + datasetIndex = 0, + currDataLength, + prevDataLength, + prevLineSetLen, + prevData, + k = 0, + len, + inited, + positionIndex = -1, + dataSet, + currCatLen, + catLen = iapi.config.catLen, + xAxis = components.xAxis[0], + dataDiff, + diff, + catDiff, + startIndex, + diffObj, + properDsStructure, + currentDataStore = components.dataset, + prevDSInnerLen; + + if (!dataset && lineSets.length === 0) { + iapi.setChartMessage(); + return; + } + + iapi.config.categories = dataObj.categories && dataObj.categories[0].category; + + datasetStore = components.dataset = []; + + for (j=0; j 0) { + datasetObj.removeData(startIndex, diff, false); + } + + datasetObj.JSONData = datasetJSON; + datasetObj.configure(); + } + datasetIndex ++; + tempDataStoreMap[j].push(datasetObj); + datasetStore.push(datasetObj); + // add to group manager + GroupManager && GroupManager.addDataSet (datasetObj, positionIndex, i); + } + } + } + else { + // Resetting inner Length and i for dispose of any previous dataset + innerLength = i = 0; + positionIndex --; + } + prevDSInnerLen = dataSetMap && dataSetMap[j] && dataSetMap[j].length; + + // When the number of inner datasets entered vis setChartData is less than the existing inner + // dataset then unadd the extra datasets and dispose them. + if (prevDSInnerLen > innerLength) { + for (k = i, len = prevDSInnerLen - innerLength + i; k < len; k ++ ) { + dataSet = dataSetMap[j][k]; + legend.removeItem(dataSet.legendItemId); + componentDispose.call(dataSet); + } + } + } + + if (dataSetMapLen > length) { + for (k = j, len = dataSetMapLen - length + j; k < len; k ++ ) { + innerLength = dataSetMap[k].length; + for (i = 0; i < innerLength; i ++) { + dataSet = dataSetMap[k][i]; + legend.removeItem(dataSet.legendItemId); + componentDispose.call(dataSet); + } + } + } + + config.dataSetMap = tempDataStoreMap; + + if (iapi.lineset) { + // For setting the lineset data if any + for (j = 0, length = lineSets.length; j < length; j++) { + lineSetJSON = lineSets[j]; + DsClass = FusionCharts.get('component', ['dataset', 'line']); + datasetObj = new DsClass (); + if (!(lineSetMap && lineSetMap[j])) { + datasetObj.chart = iapi; + datasetObj.index = datasetIndex; + datasetObj.init (lineSetJSON); + } + else { + datasetObj = lineSetMap[j]; + datasetObj.index = datasetIndex; + prevData = datasetObj.JSONData; + prevDataLength = prevData.data.length; + currDataLength = (lineSetJSON.data && lineSetJSON.data.length) || 0; + // Removing data plots if the number of current data plots is more than + // the existing ones. + if (prevDataLength > currDataLength) { + datasetObj.removeData(currDataLength, + prevDataLength - currDataLength, false); + } + datasetObj.JSONData = lineSetJSON; + datasetObj.configure(); + } + tempLineStoreMap.push (datasetObj); + datasetStore.push(datasetObj); + datasetIndex++; + } + + prevLineSetLen = lineSetMap && lineSetMap.length; + if (prevLineSetLen > length) { + for (k = j, len = prevLineSetLen - length + j; k < len; k ++ ) { + dataSet = lineSetMap[k]; + legend.removeItem(dataSet.legendItemId); + componentDispose.call(dataSet); + } + } + + config.lineSetMap = tempLineStoreMap; + } + // When the data entered is of column2d or mscolumn2d + if (!properDsStructure) { + components.dataset = currentDataStore; + iapi.setChartMessage(); + return; + } + iapi.config.catLen = xAxis.getCategoryLen(); + }, + creditLabel: creditLabel + }, chartAPI.mscartesian, { + isstacked: true, + enablemousetracking: true + }); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-mscombi2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + UNDEFINED, + //add the tools thats are requared + pluck = lib.pluck, + componentDispose = lib.componentDispose; + + chartAPI ('mscombi2d', { + friendlyName: 'Multi-series Combination Chart', + standaloneInit: true, + creditLabel: creditLabel, + defaultDatasetType: 'column', + applicableDSList: { 'line': true, 'area': true, 'column': true }, + _createDatasets : function () { + var iapi = this, + components = iapi.components, + dataObj = iapi.jsonData, + dataset = dataObj.dataset, + length = dataset && dataset.length, + i, + j, + datasetStore, + datasetObj, + defaultSeriesType = iapi.defaultDatasetType, + applicableDSList = iapi.applicableDSList, + legend = iapi.components.legend, + xAxis = components.xAxis[0], + GroupManager, + dsType, + DsClass, + DsGroupClass, + datasetJSON, + isStacked = iapi.config.isstacked, + groupManagerName, + parentyaxis, + prevDataLength, + currDataLength, + groupManagers = [], + dsCount = { }, + config = iapi.config, + groupManager, + count, + diff, + catLen = iapi.config.catLen, + currCatLen, + dataDiff, + catDiff, + prevData, + diffObj, + startIndex, + // map,line,area,column, stores the index of the various dataplots in combinational charts. + datasetMap = config.datasetMap || (config.datasetMap = { + line : [], + area : [], + column : [], + column3d: [], + scrollcolumn2d: [] + }), + dsTypeRef, + tempMap = { + line : [], + area : [], + column : [], + column3d: [], + scrollcolumn2d: [] + }; + + if (!dataset) { + iapi.setChartMessage(); + } + iapi.config.categories = dataObj.categories && dataObj.categories[0].category; + datasetStore = components.dataset = []; + legend && legend.emptyItems(); + + for (i=0; i 0) { + datasetObj.removeData(startIndex, diff, false); + } + datasetObj.index = i; + datasetObj.JSONData = datasetJSON; + datasetObj.configure(); + dsTypeRef.splice(0, 1); + } + // Push new dataset object into both tempmap array and datastore array + tempMap[dsType].push(datasetObj); + datasetStore.push (datasetObj); + } + } + + + iapi._setDatasetOrder(); + // Removing unused datasets if any + for (dataset in datasetMap) { + dsTypeRef = datasetMap[dataset]; + groupManager = dsTypeRef[0] && dsTypeRef[0].groupManager; + length = dsTypeRef.length; + count = dsCount[dataset] === UNDEFINED ? 0 : dsCount[dataset] + 1; + if (length) { + isStacked && groupManager && groupManager.removeDataSet(0, count, length); + for (j = 0; j < length; j++) { + groupManager && !isStacked && groupManager.removeDataSet(count, 0, 1); + if (dsTypeRef[j].type === 'column' && iapi.is3D === true) { + dsTypeRef[j].visible = false; + dsTypeRef[j].draw(); + } + else { + componentDispose.call(dsTypeRef[j]); + } + } + } + } + config.datasetMap = tempMap; + iapi.config.catLen = xAxis.getCategoryLen(); + } + }, chartAPI.areabase, { + enablemousetracking: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-mscombi3d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('mscombi3d', { + standaloneInit: true, + friendlyName: 'Multi-series 3D Combination Chart', + defaultDatasetType: 'column3d', + is3D: true, + creditLabel: creditLabel, + defaultPlotShadow: 1, + applicableDSList: { 'column3d': true, 'line': true, 'area': true }, + _createDatasets : chartAPI.mscombi2d + }, chartAPI.mscartesian3d, { + showplotborder: 0, + enablemousetracking: true + }, chartAPI.areabase); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-mscolumnline3d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('mscolumnline3d', { + friendlyName: 'Multi-series Column and Line Chart', + is3D: true, + creditLabel: creditLabel, + defaultPlotShadow: 1, + applicableDSList: { 'column3d': true, 'line': true } + }, chartAPI.mscombi3d, { + use3dlineshift: 1, + showplotborder: 0, + enablemousetracking: true + }, chartAPI.msarea); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-stackedcolumn2dline', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname); + + chartAPI ('stackedcolumn2dline', { + friendlyName: 'Stacked Column and Line Chart', + defaultDatasetType: 'column', + creditLabel: creditLabel, + applicableDSList: { 'line': true, 'column': true } + }, chartAPI.mscombi2d, { + isstacked: true, + stack100percent: 0, + enablemousetracking: true + }, chartAPI.msarea); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-stackedcolumn3dline', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('stackedcolumn3dline', { + friendlyName: 'Stacked 3D Column and Line Chart', + is3D: true, + creditLabel: creditLabel, + _mouseEvtHandler: chartAPI.stackedcolumn3d._mouseEvtHandler, + applicableDSList: { 'column3d' :true, 'line': true } + }, chartAPI.mscombi3d, { + use3dlineshift: 1, + isstacked: true, + stack100percent: 0, + showplotborder: 0, + enablemousetracking: true + }, chartAPI.msarea); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-mscombidy2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('mscombidy2d', { + standaloneInit: true, + friendlyName: 'Multi-series Dual Y-Axis Combination Chart', + defaultDatasetType: 'column', + sDefaultDatasetType: 'line', + _createDatasets: chartAPI.mscombi2d, + creditLabel: creditLabel, + applicableDSList: { 'column': true, 'line': true, 'area': true } + }, chartAPI.msdybasecartesian, { + isdual: 1, + enablemousetracking: true + }, chartAPI.msarea); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-mscolumn3dlinedy', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('mscolumn3dlinedy', { + standaloneInit: true, + friendlyName: 'Multi-series 3D Column and Line Chart', + defaultDatasetType: 'column3d', + sDefaultDatasetType: 'line', + is3D: true, + creditLabel: creditLabel, + _createDatasets: chartAPI.mscombi2d, + defaultPlotShadow: 1, + applicableDSList: { 'column3d' :true, 'line': true } + }, chartAPI.msdybasecartesian3d, { + use3dlineshift: 1, + isdual: true, + showplotborder: 0, + enablemousetracking: true + }, chartAPI.msarea); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-stackedcolumn3dlinedy', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname); + + chartAPI ('stackedcolumn3dlinedy', { + standaloneInit: true, + friendlyName: 'Stacked 3D Column and Line Chart', + is3D: true, + defaultDatasetType: 'column3d', + creditLabel: creditLabel, + sDefaultDatasetType: 'line', + defaultSecondaryDataset: 'line', + _createDatasets: chartAPI.mscombi2d, + _mouseEvtHandler: chartAPI.stackedcolumn3d._mouseEvtHandler, + applicableDSList: { 'column3d': true, 'line': true } + }, chartAPI.msdybasecartesian3d, { + use3dlineshift: 1, + isdual: true, + isstacked: true, + showplotborder: 0, + enablemousetracking: true + }, chartAPI.msarea); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-msstackedcolumn2dlinedy', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('msstackedcolumn2dlinedy', { + standaloneInit: true, + friendlyName: 'Multi-series Dual Y-Axis Stacked Column and Line Chart', + stack100percent: 0, + defaultDatasetType: 'column', + sDefaultDatasetType: 'line', + hasLineSet: true, + creditLabel: creditLabel, + applicableDSList: { 'column': true }, + lineset : true, + _createDatasets: chartAPI.msstackedcolumn2d + }, chartAPI.msdybasecartesian, { + isdual: true, + haslineset: true, + isstacked: true, + enablemousetracking: true + }, chartAPI.msarea); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-scrollcolumn2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + math = Math, + mathMin = math.min, + mathMax = math.max, + mathFloor = math.floor, + mathRound = math.round, + pluckNumber = lib.pluckNumber, + UNDEFINED, + R = lib.Raphael; + + chartAPI ('scrollcolumn2d', { + standaloneInit: true, + friendlyName: 'Scrollable Multi-series Column Chart', + tooltipConstraint: 'plot', + canvasborderthickness: 1, + creditLabel: creditLabel, + defaultDatasetType : 'scrollcolumn2d', + applicableDSList: { 'scrollcolumn2d': true }, + avgScrollPointWidth: 40, + hasScroll: true, + defaultPlotShadow: 1, + binSize : 0, + + //function to handle swipe gesture + _swipeX: function (e, chartX) { + var iapi = this, + chartConfig = iapi.config, + components = iapi.components, + xAxis = components.xAxis[0], + axisConfig = xAxis.config, + canvasConfig = components.canvas.config, + canvasBorderWidth = canvasConfig.canvasBorderWidth, + + scrollBar = components.scrollBar, + scrollConf = scrollBar.conf, + scrollRatio = scrollConf.scrollRatio, + w = scrollConf.width - 1, + h = scrollConf.height - 1, + initialScrollPosition = chartConfig.scrollToEnd, + lastScrollPosition = chartConfig.lastScrollPosition, + canvasLeft = chartConfig.canvasLeft, + + aXisLineStartExtension = pluckNumber(canvasBorderWidth, axisConfig.lineStartExtension), + prevPos = iapi.touchCurrPos, + swipeStart = iapi.swipeStart, + type = e.type, + x = canvasLeft - aXisLineStartExtension, + button = (chartConfig.scrollShowButtons ? mathMin(h, w * 0.5) : 0), + mousedown = iapi.config.mousedown, + track, + trackLength, + trackOffset, + anchorOffset, + pos, + position; + + lastScrollPosition = lastScrollPosition !== undefined ? lastScrollPosition : initialScrollPosition; + + switch (type) { + case 'touchstart': + case 'mousedown': + iapi.config.mousedown = true; + iapi.prevScrollPosition = lastScrollPosition; + iapi.touchCurrPos = chartX; + break; + case 'touchmove': + case 'mousemove': + if (mousedown) { + if(!swipeStart) { + global.raiseEvent('scrollStart', { + scrollPosition: lastScrollPosition + }, iapi.chartInstance); + } + // lib.toolTip && lib.toolTip.hide(); + + track = w * scrollRatio - (button * 2 * scrollRatio); + trackLength = w - 2 * button - track; + trackOffset = x + button + 0.5; + + anchorOffset = trackOffset + (track - 1) * 0.5; + + pos =lastScrollPosition + ((prevPos - chartX) / trackLength); + + position = mathMin(mathMax(pos, 0),1); + + iapi.updateManager(position); + + // global.raiseEvent('onScroll', { + // scrollPosition: position + // }, iapi.chartInstance); + + iapi.swipeStart = true; + + iapi.touchCurrPos = chartX; + + if(e.originalEvent.preventDefault){ + e.originalEvent.preventDefault(); + } else { + e.originalEvent.returnValue = false; + } + + return true; + } + break; + case 'touchend': + case 'mouseup': + if(mousedown) { + if(iapi.swipeStart) { + global.raiseEvent('scrollEnd', { + prevScrollPosition: iapi.prevScrollPosition, + scrollPosition: chartConfig.lastScrollPosition + }, iapi.chartInstance); + iapi.swipeStart = false; + } + chartConfig.mousedown = false; + } + } + }, + + eiMethods: { + scrollTo: chartAPI.mscartesian._scrollTo + }, + + _manageScrollerPosition: function () { + var iapi = this, + config = iapi.config, + chartComponents = iapi.components, + _scrollBar = iapi._scrollBar, + availableHeight, + availableWidth, + scrollEnabled, + getScrollItems = _scrollBar.get, + scrollBar = chartComponents.scrollBar, + scrollItem, + scrollDimensions; + + // Fetching the scroll Items. + scrollItem = getScrollItems()[0]; + scrollBar.setConfiguaration(scrollItem.conf); + scrollEnabled = config.scrollEnabled; + //manage space for the scroll bar. + // fetch the availble space for the canvas area after other space management is over + availableHeight = config.canvasHeight; + availableWidth = config.canvasWidth; + scrollDimensions = scrollBar.getLogicalSpace(); + //allocate space for toolBox and set the chart configurations. + // shift denotes the amount of shift required by the x-axis + iapi._allocateSpace ({ + bottom: (config.shift = scrollEnabled === false ? 0 : + (scrollDimensions.height + scrollBar.conf.padding)) + }); + + }, + _resetViewPortConfig: function () { + var iapi = this; + + iapi.config.viewPortConfig = { + scaleX: 1, + scaleY: 1, + x: 0, + y: 0 + }; + }, + updateManager: function (pos) { + var iapi = this, + config = iapi.config, + viewPortConfig = iapi.config.viewPortConfig, + scaleX = viewPortConfig.scaleX, + canvasWidth = config.canvasWidth, + datasetLayer = iapi.graphics.datasetGroup, + dataLabelsLayer = iapi.graphics.datalabelsGroup, + trackerGroup = iapi.graphics.trackerGroup, + offset = (canvasWidth * (scaleX - 1) * pos), + xAxis = iapi.components.xAxis[0], + xAxisAnifConfig, + xAxisNameDrawConfig, + sumLabelsLayer = iapi.graphics.sumLabelsLayer, + transformAttr; + viewPortConfig.x = offset / scaleX; + // Taking mathround of offset for crisp value + transformAttr = 't' + -mathRound(offset) + ',0'; + config.lastScrollPosition = pos; + if (pos !== UNDEFINED){ + iapi.components.scrollBar.node && iapi.components.scrollBar.node.attr({ + 'scroll-position': pos + }); + } + datasetLayer.attr({ + transform: transformAttr + }); + dataLabelsLayer.attr({ + transform: transformAttr + }); + trackerGroup.attr({ + transform: transformAttr + }); + sumLabelsLayer && sumLabelsLayer.attr({ + transform: transformAttr + }); + + xAxisAnifConfig = xAxis.getAxisConfig('animateAxis'); + xAxisNameDrawConfig = xAxis.getAxisConfig('drawAxisName'); + // temp fix for axis animation + xAxis.setAxisConfig({ + animateAxis : false, + drawAxisName : false + }); + xAxis.draw(); + // reset the animation configuration + xAxis.setAxisConfig({ + animateAxis : xAxisAnifConfig, + drawAxisName : xAxisNameDrawConfig + }); + }, + _createToolBox: function () { + var iapi = this, + components = iapi.components, + _scrollBar = iapi._scrollBar, + getScrollItems = _scrollBar.get, + addScrollItems = _scrollBar.add, + tb, + Scroller, + scrollItem, + ComponentGroup, + scrollBar = components.scrollBar, + toolBoxAPI; + + // create the export or print buttons if required. + chartAPI.mscartesian._createToolBox.call(iapi); + tb = components.tb; + toolBoxAPI = components.toolBoxAPI || tb.getAPIInstances(tb.ALIGNMENT_HORIZONTAL); + Scroller = toolBoxAPI.Scroller; + ComponentGroup = toolBoxAPI.ComponentGroup; + // temp code: On update scroll items needs to be reused. + _scrollBar.clear(); + // Adding the scroll items in the scroll bar. + addScrollItems({ + isHorizontal: true + }, { + // Attach the callback for scroll Interaction. + 'scroll' : (function (ref) { + return function () { + var pos = arguments && arguments[0], + components = ref.components, + scrollBar = components.scrollBar, + scrollConf = scrollBar.conf, + isScrollTo = scrollConf.scrollTo; + ref.config.scrollCurrPosition = pos; + if (!isScrollTo){ + global.raiseEvent('onScroll', { + scrollPosition: pos + }, ref.chartInstance); + } + scrollConf.scrollTo = false; + ref.updateManager.apply(ref, arguments); + }; + })(iapi) + }); + + // Fetching the scroll Items. + scrollItem = getScrollItems()[0]; + + if (!scrollBar) { + // adding the scrollbar. + components.scrollBar = new Scroller(scrollItem.conf, tb.idCount, tb.pId) + .attachEventHandlers(scrollItem.handler); + } + }, + + _setAxisScale: function () { + var iapi = this, + components = iapi.components, + config = iapi.config, + xAxis = components.xAxis[0], + catCount = xAxis.getCategoryLen(), + jsonData = iapi.jsonData, + scrollOptions = config.scrollOptions || (config.scrollOptions = {}), + datasets = iapi.components.dataset, + outerDataSetLen = datasets.length, + chartAttr = jsonData.chart, + chartWidth = config.width, + scaleX, + dataset, + i, + numOfColumnSeries = 0, + offset, + noOfDatasets, + canvasWidth = config.canvasWidth, + totalNumPoint, + scrollToEnd = config.scrollToEnd, + lastScrollPosition = config.lastScrollPosition, + pos, + //number of columns to be drawn. + numVisiblePlot = pluckNumber (chartAttr.numvisibleplot, + mathFloor (chartWidth / iapi.avgScrollPointWidth)); + for (i = 0; i < outerDataSetLen; i++) { + dataset = datasets[i]; + if (dataset.type === 'column') { + numOfColumnSeries++; + } + } + noOfDatasets = numOfColumnSeries; + if (iapi.config.isstacked) { + numOfColumnSeries = 1; + } + + totalNumPoint = catCount * (numOfColumnSeries || 1); + + + if (numVisiblePlot >= 2 && numVisiblePlot < totalNumPoint) { + //update the scaleX for the axis. + config.viewPortConfig.scaleX = scaleX = totalNumPoint / numVisiblePlot; + if (lastScrollPosition !== undefined) { + pos = lastScrollPosition; + } + else { + pos = scrollToEnd ? 1 : 0; + } + + offset = (canvasWidth * (scaleX - 1) * pos); + config.viewPortConfig.x = offset / scaleX; + + //parse the scroll properties + scrollOptions.vxLength = numVisiblePlot / outerDataSetLen; + config.scrollEnabled = true; + } + else { + config.scrollEnabled = false; + } + }, + drawScrollBar: function () { + var iapi = this, + config = iapi.config, + viewPortConfig = config.viewPortConfig, + components = iapi.components, + graphics = iapi.graphics, + paper = components.paper, + xAxis = components.xAxis[0], + axisConfig = xAxis.config, + axisRange = xAxis.config.axisRange, + scrollOptions = config.scrollOptions || (config.scrollOptions = {}), + max = axisRange.max, + min = axisRange.min, + vxLength = scrollOptions.vxLength, + scrollBar = components.scrollBar, + scrollNode = scrollBar.node, + scrollToEnd = config.scrollToEnd, + lastScrollPosition = config.lastScrollPosition, + scaleX = viewPortConfig.scaleX, + canvasLeft, + canvasTop, + canvasHeight, + canvasWidth, + canvasConfig, + canvasBorderWidth, + aXisLineWidth, + startPercent, + aXisLineStartExtension, + aXisLineEndExtension, + scrollRatio, + windowedCanvasWidth, + fullCanvasWidth, + scrollBarParentGroup; + if (lastScrollPosition !== undefined) { + startPercent = lastScrollPosition; + } + else { + startPercent = scrollToEnd ? 1 : 0; + } + canvasLeft = config.canvasLeft; + canvasTop = config.canvasTop; + canvasHeight = config.canvasHeight; + canvasWidth = config.canvasWidth; + canvasConfig = components.canvas.config; + canvasBorderWidth = canvasConfig.canvasBorderWidth; + aXisLineWidth = axisConfig.showAxisLine ? axisConfig.axisLineThickness || 0 : 0; + aXisLineStartExtension = pluckNumber(canvasBorderWidth, axisConfig.lineStartExtension); + aXisLineEndExtension = pluckNumber(canvasBorderWidth, axisConfig.lineEndExtension); + scrollOptions.viewPortMin = min; + scrollOptions.viewPortMax = max; + scrollRatio = (scrollOptions.scrollRatio = 1 / scaleX); + windowedCanvasWidth = scrollOptions.windowedCanvasWidth = xAxis.getAxisPosition(vxLength); + fullCanvasWidth = scrollOptions.fullCanvasWidth = + xAxis.getAxisPosition(max - min) - windowedCanvasWidth; + + scrollBarParentGroup = graphics.scrollBarParentGroup; + if (!scrollBarParentGroup) { + scrollBarParentGroup = graphics.scrollBarParentGroup = paper.group('scrollBarParentGroup', + graphics.parentGroup).insertBefore(graphics.datalabelsGroup); + } + //draw the scroller element + // todo padding needs to be included. + if (config.scrollEnabled !== false) { + scrollBar.draw (canvasLeft - aXisLineStartExtension, + canvasTop + canvasHeight + canvasBorderWidth + aXisLineWidth - 2,{ + width: canvasWidth + aXisLineStartExtension + aXisLineEndExtension, + scrollRatio: scrollRatio, + roundEdges: canvasConfig.isRoundEdges, + fullCanvasWidth: fullCanvasWidth, + windowedCanvasWidth: windowedCanvasWidth, + scrollPosition: startPercent, + parentLayer: scrollBarParentGroup + }); + + // attach the callback for raising event only for it is a new scroll node. + !scrollNode && (function () { + var prevPos; + R.eve.on('raphael.scroll.start.' + scrollBar.node.id, function (pos) { + prevPos = pos; + global.raiseEvent('scrollstart', { + scrollPosition: pos + }, iapi.chartInstance); + }); + + R.eve.on('raphael.scroll.end.' + scrollBar.node.id, function (pos) { + global.raiseEvent('scrollend', { + prevScrollPosition: prevPos, + scrollPosition: pos + }, iapi.chartInstance); + }); + }()); + } + else { + scrollBar && scrollBar.node && scrollBar.node.hide(); + } + }, + _drawDataset: function () { + var iapi = this; + iapi._setClipping(); + chartAPI.mscartesian._drawDataset.call(this); + }, + _setClipping: function () { + var chart = this, + config = chart.config, + datasetLayer = chart.graphics.datasetGroup, + dataLabelsLayer = chart.graphics.datalabelsGroup, + trackerLayer = chart.graphics.trackerGroup, + viewPortConfig = config.viewPortConfig, + sumLabelsLayer = chart.graphics.sumLabelsLayer, + scaleX = viewPortConfig.scaleX, + animationObj = chart.get('config', 'animationObj'), + animation = animationObj.duration, + dummyAnimElem = animationObj.dummyObj, + dummyAnimObj = animationObj.animObj, + animType = animationObj.animType, + x = viewPortConfig.x, + chartHeight = config.height, + clipCanvas = chart.components.canvas.config.clip['clip-canvas']; + + clipCanvas = (clipCanvas && clipCanvas.slice(0)) || []; + if (!chart.config.clipSet) { + datasetLayer.attr({ + 'clip-rect': clipCanvas + }); + dataLabelsLayer.attr({ + 'clip-rect': clipCanvas + }); + trackerLayer.attr({ + 'clip-rect': clipCanvas + }); + clipCanvas[3] = chartHeight; + clipCanvas[1] = 0; + sumLabelsLayer && sumLabelsLayer.attr({ + 'clip-rect': clipCanvas + }); + } + else { + datasetLayer.animateWith(dummyAnimElem, dummyAnimObj,{ + 'clip-rect': clipCanvas + }, animation, animType); + dataLabelsLayer.animateWith(dummyAnimElem, dummyAnimObj,{ + 'clip-rect': clipCanvas + }, animation, animType); + trackerLayer.attr({ + 'clip-rect': clipCanvas + }); + + clipCanvas[3] = chartHeight; + clipCanvas[1] = 0; + sumLabelsLayer && sumLabelsLayer.animateWith(dummyAnimElem, dummyAnimObj, { + 'clip-rect': clipCanvas + }, animation, animType); + } + datasetLayer.attr({ + transform: 'T' + - (x * scaleX) + ',0' + }); + dataLabelsLayer.attr({ + transform: 'T' + - (x * scaleX) + ',0' + }); + trackerLayer.attr({ + transform: 'T' + - (x * scaleX) + ',0' + }); + sumLabelsLayer && sumLabelsLayer.attr({ + transform: 'T' + - (x * scaleX) + ',0' + }); + chart.config.clipSet = true; + }, + configure: function () { + var chart = this, + jsonData = chart.jsonData, + chartAttr = jsonData.chart, + chartConfig; + chartAPI.mscolumn2d.configure.call(this); + chartConfig = chart.config; + chartConfig.scrollToEnd = pluckNumber(chartAttr.scrolltoend, 0); + chartConfig.lastScrollPosition = UNDEFINED; + } + }, chartAPI.scrollbase); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-scrollarea2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI, + pluckNumber = lib.pluckNumber, + math = Math, + mathFloor = math.floor; + + chartAPI ('scrollarea2d', { + friendlyName: 'Scrollable Multi-series Area Chart', + tooltipConstraint: 'plot', + canvasborderthickness: 1, + creditLabel: creditLabel, + hasScroll: true, + defaultDatasetType: 'scrollarea2d', + applicableDSList: { 'scrollarea2d': true }, + avgScrollPointWidth: 75, + defaultPlotShadow: 0, + binSize : 0, + _setAxisScale: function () { + var iapi = this, + components = iapi.components, + config = iapi.config, + xAxis = components.xAxis[0], + catCount = xAxis.getCategoryLen(), + jsonData = iapi.jsonData, + scrollOptions = config.scrollOptions || (config.scrollOptions = {}), + chartAttr = jsonData.chart, + chartWidth = config.width, + scaleX, + totalNumPoint = catCount, + offset, + lastScrollPosition = config.lastScrollPosition, + pos, + scrollToEnd = config.scrollToEnd, + canvasWidth = config.canvasWidth, + //number of columns to be drawn. + numVisiblePlot = pluckNumber (chartAttr.numvisibleplot, + mathFloor (chartWidth / iapi.avgScrollPointWidth)); + if (numVisiblePlot >= 2 && numVisiblePlot < totalNumPoint) { + //update the scaleX for the axis. + config.viewPortConfig.scaleX = scaleX = totalNumPoint / numVisiblePlot; + if (lastScrollPosition !== undefined) { + pos = lastScrollPosition; + } + else { + pos = scrollToEnd ? 1 : 0; + } + + + offset = (canvasWidth * (scaleX - 1) * pos); + config.viewPortConfig.x = offset / scaleX; + + //parse the scroll properties + scrollOptions.vxLength = numVisiblePlot; + config.scrollEnabled = true; + } + else { + config.scrollEnabled = false; + } + } + }, chartAPI.scrollcolumn2d, { + enablemousetracking: true + }, chartAPI.areabase); + + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-scrollline2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname); + + chartAPI ('scrollline2d', { + friendlyName: 'Scrollable Multi-series Line Chart', + tooltipConstraint: 'plot', + canvasborderthickness: 1, + defaultDatasetType: 'line', + creditLabel: creditLabel, + avgScrollPointWidth: 75, + defaultPlotShadow: 1, + binSize : 0 + }, chartAPI.scrollarea2d, { + zeroplanethickness: 1, + zeroplanealpha: 40, + showzeroplaneontop: 0, + enablemousetracking: true + }, chartAPI.areabase); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-scrollstackedcolumn2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('scrollstackedcolumn2d', { + friendlyName: 'Scrollable Stacked Column Chart', + canvasborderthickness: 1, + tooltipConstraint: 'plot', + avgScrollPointWidth: 75, + creditLabel: creditLabel + }, chartAPI.scrollcolumn2d, {}, chartAPI.stackedcolumn2d); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-scrollcombi2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('scrollcombi2d', { + friendlyName: 'Scrollable Combination Chart', + tooltipConstraint: 'plot', + hasScroll: true, + canvasborderthickness: 1, + avgScrollPointWidth: 40, + applicableDSList: {'area': true, 'line': true, 'column': true}, + creditLabel: creditLabel, + _createDatasets : chartAPI.mscombi2d + }, chartAPI.scrollcolumn2d, {}, chartAPI.msarea); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-scrollcombidy2d', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI; + + chartAPI ('scrollcombidy2d', { + friendlyName: 'Scrollable Dual Y-Axis Combination Chart', + tooltipConstraint: 'plot', + canvasborderthickness: 1, + avgScrollPointWidth: 40, + hasScroll: true, + _swipeX: chartAPI.scrollcolumn2d, + _drawDataset: chartAPI.scrollcolumn2d, + updateManager: chartAPI.scrollcolumn2d, + _setAxisScale: chartAPI.scrollcolumn2d, + _createToolBox: chartAPI.scrollcolumn2d, + _scrollBar: chartAPI.scrollcolumn2d, + _manageScrollerPosition: chartAPI.scrollcolumn2d, + drawScrollBar: chartAPI.scrollcolumn2d, + _setClipping: chartAPI.scrollcolumn2d, + creditLabel: creditLabel, + configure: chartAPI.scrollcolumn2d, + eiMethods: { + scrollTo: chartAPI.mscartesian._scrollTo + } + }, chartAPI.mscombidy2d, { + enablemousetracking: true, + isdual: true + }, chartAPI.areabase); + +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-scatter', function () { + var global = this, + lib = global.hcLib, + win = global.window, + chartAPI = lib.chartAPI, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname); + + chartAPI ('scatter', { + friendlyName: 'Scatter Chart', + isXY: true, + standaloneInit: true, + hasLegend: true, + defaultZeroPlaneHighlighted: false, + creditLabel: creditLabel, + defaultDatasetType : 'Scatter', + applicableDSList: { + 'Scatter': true + }, + drawTracker: true + }, chartAPI.scatterBase, { + allowreversexaxis: true, + enablemousetracking: true + }); +}]); + +FusionCharts.register ('module', ['private', 'modules.renderer.js-bubble', function () { + var global = this, + lib = global.hcLib, + win = global.window, + creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname), + chartAPI = lib.chartAPI, + math = Math, + mathMax = math.max, + mathMin = math.min; + + chartAPI ('bubble', { + friendlyName: 'Bubble Chart', + standaloneInit: true, + defaultDatasetType: 'bubble', + creditLabel: creditLabel, + applicableDSList: { 'bubble': true }, + getDataLimits: function () { + var iapi = this, + datasets = iapi.components.dataset, + i, + dataset, + len, + limits, + zMax = -Infinity, + zMin = +Infinity; + + for (i = 0,len = datasets.length; i < len; i++) { + dataset = datasets[i]; + limits = dataset.getDataLimits(); + zMax = mathMax(zMax, limits.zMax || -Infinity); + zMin = mathMin(zMin, limits.zMin || +Infinity); + } + zMax = zMax === -Infinity ? 0 : zMax; + zMin = zMin === +Infinity ? 0 : zMin; + return { + zMax: zMax, + zMin: zMin + }; + } + }, chartAPI.scatter, { + enablemousetracking: true + }); +}]); + +}, [3, 2, 2, 'sr4']]); +/* jshint ignore: end */ + +/** + * FusionCharts JavaScript Library + * ZoomLine visualization module extension comprising visualization logic as well as renderer adapter. + * @private + * + * @module fusioncharts.renderer.javascript.charts.zoomline + * @requires fusioncharts.renderer.javascript.charts.common + */ +FusionCharts.register ('module', ['private', 'modules.renderer.js-zoomline', function () { + /*jslint newcap: false */ + var global = this, + lib = global.hcLib, + hashify = lib.hashify, + win = global.window, + doc = win.document, + Image = win.Image, + MouseEvent = win.MouseEvent, + userAgent = win.navigator.userAgent, + isIE = /msie/i.test (userAgent) && !win.opera, + chartAPI = lib.chartAPI, + extend2 = lib.extend2, + addEvent = lib.addEvent, + pluck = lib.pluck, + // BLANKSTRING = lib.BLANKSTRING, + // parseTooltext = lib.parseTooltext, + pluckNumber = lib.pluckNumber, + getFirstColor = lib.getFirstColor, + convertColor = lib.graphics.convertColor, + bindSelectionEvent = lib.bindSelectionEvent, + // parseTrendlines = lib.createTrendLine, + parseUnsafeString = lib.parseUnsafeString, + componentDispose = lib.componentDispose, + // regescape = lib.regescape, + R = lib.Raphael, + toRaphaelColor = lib.toRaphaelColor, + hasTouch = lib.hasTouch, + plotEventHandler = lib.plotEventHandler, + getMouseCoordinate = lib.getMouseCoordinate, + UNDEFINED, + creditLabel = false && !lib.CREDIT_REGEX.test(win.location.hostname), + // CONFIGKEY = lib.FC_CONFIG_STRING, + // G = 'g', + PIPE = '|', + // DOT = '.', + BLANK = '', + TOUCH_THRESHOLD_PIXELS = lib.TOUCH_THRESHOLD_PIXELS, + CLICK_THRESHOLD_PIXELS = lib.CLICK_THRESHOLD_PIXELS, + // hot/tracker threshold in pixels + HTP = hasTouch ? TOUCH_THRESHOLD_PIXELS : CLICK_THRESHOLD_PIXELS, + COLOR_WHITE = '#FFFFFF', + COLOR_E3E3E3 = '#E3E3E3', + COLOR_C2C2C2 = '#C2C2C2', + COLOR_EFEFEF = '#EFEFEF', + STR_DEF = 'default', + POINTER = 'pointer', + preDefStr = lib.preDefStr, + DEFAULT_CURSOR = preDefStr.DEFAULT, + DATAPLOTCLICK = 'dataplotclick', + //default cursor type. + // STR_DEF = 'default', + PX = 'px', + TRACKER_FILL = 'rgba(192,192,192,' + (isIE ? 0.002 : 0.000001) + ')', // invisible but clickable + UNDEF, + schedular = lib.schedular, + priorityList = lib.priorityList, + math = Math, + mathCeil = math.ceil, + mathFloor = math.floor, + mathRound = math.round, + mathMax = math.max, + mathMin = math.min, + mathCos = math.cos, + mathSin = math.sin, + // mathAbs = math.abs, + toFloat = win.parseFloat, + toInt = win.parseInt, + MAX_MITER_LINEJOIN = 2, + CrossLine; // function (constructor) + + // Add events to the legacy event list + extend2 (lib.eventList, { + zoomed: 'FC_Zoomed', + pinned: 'FC_Pinned', + resetzoomchart: 'FC_ResetZoomChart' + }); + + chartAPI ('zoomline',{ + standaloneInit: true, + canvasborderthickness: 1, + defaultDatasetType: 'zoomline', + applicableDSList: { + 'zoomline': true + }, + friendlyName: 'Zoomable and Panable Multi-series Line Chart', + creditLabel: creditLabel, + /* + * Only y-axes are being drawn. + * x-axis is drawn after setting the label step value. + */ + _drawAxis : function () { + var iapi = this, + components = iapi.components, + yAxisArr = components.yAxis || [], + i, + len; + for (i = 0, len = yAxisArr.length; i < len; i++) { + yAxisArr[i].draw (); + } + }, + _setCategories: function () { + var iapi = this, + config = iapi.config, + components = iapi.components, + dataObj = iapi.jsonData, + xAxis = components.xAxis, + tempArr, + data, + j, + len, + cdm = config.cdm, + cdmchar = config.cdmchar, + category = (dataObj.categories && dataObj.categories[0].category) || []; + if (cdm || typeof category === 'string') { + if (category.split) { + tempArr = category.split(cdmchar); + data = []; + for (j = 0, len = tempArr.length; j < len; j += 1) { + data.push({ + label : tempArr[j] + }); + } + iapi.config.categories = dataObj.categories[0].category = data; + } + } + xAxis[0].setAxisPadding(0, 0); + xAxis[0].setCategory (data || category); + }, + + _createDatasets : function () { + var i, + j, + len, + data, + diff, + dsType, + DsClass, + tempArr, + datasetObj, + datasetJSON, + parentyaxis, + datasetStore, + prevDataLength, + datasetStoreLen, + currDataLength, + dsCount = {}, + iapi = this, + config = iapi.config, + components = iapi.components, + dataObj = iapi.jsonData, + dataset = dataObj.dataset, + length = dataset && dataset.length, + cdmchar = config.cdmchar, + cdm = config.cdm, + defaultSeriesType = iapi.defaultDatasetType, + applicableDSList = iapi.applicableDSList, + legend = iapi.components.legend, + legendItems = legend.components.items || [], + category = dataObj.categories && dataObj.categories[0].category; + if (!dataset || !category) { + iapi.setChartMessage(); + } + iapi.config.categories = category; + datasetStore = components.dataset || (components.dataset = []); + datasetStoreLen = datasetStore.length; + for (i=0; i currDataLength) { + datasetObj.removeData(currDataLength, prevDataLength - currDataLength, false); + } + datasetStore[i].JSONData = datasetJSON; + datasetStore[i].configure(); + datasetStore[i]._deleteGridImages && datasetStore[i]._deleteGridImages(); + } + } + } + + // When the number of datasets entered vis setChartData is less than the existing dataset then dispose the + // extra datasets. + if (datasetStoreLen > length) { + diff = datasetStoreLen - length; + for (j = i, length = diff + i; j < length; j ++ ) { + componentDispose.call(datasetStore[j]); + } + datasetStore.splice(i, diff); + legendItems.splice(i, diff); + } + }, + + isWithinCanvas : function (e, chart) { + var chartConfig = chart.get('config'), + mousePos = lib.getMouseCoordinate(chart.get('linkedItems', 'container'),e, chart), + /*converts the original mouse event toa Fusion Charts event( that has chartX, chartY, pageX and pageY as + its property)*/ + chartX = mousePos.chartX, + chartY = mousePos.chartY, + minX = chartConfig.canvasLeft, + minY = chartConfig.canvasTop, + maxX = chartConfig.canvasLeft + chartConfig.canvasWidth, + maxY = chartConfig.canvasHeight + chartConfig.canvasTop; + //default value of the flag. + mousePos.insideCanvas = false; + + // store the original event as well + mousePos.originalEvent = e; + //return true if within the canvas + if (chartX > minX && chartX < maxX && chartY > minY && chartY < maxY) { + //set the flag to be TRUE if triggered within the canvas area. + mousePos.insideCanvas = true; + } + return mousePos; + }, + + highlightPoint: function (showHover,cx,cy,point,index, toolText) { + var chart = this, + chartConfig = chart.config, + chartComponents = chart.components, + chartGraphics = chart.graphics, + paper = chartComponents.paper, + tracker = chartGraphics.tracker, + datasetObj = chartComponents.dataset[index], + datasetConfig = datasetObj && datasetObj.config, + radius = ( datasetObj && datasetConfig.zoomedRadius || 0), + hoverCosmetics = datasetObj && datasetConfig.hoverCosmetics, + fill = hoverCosmetics && hoverCosmetics.fill, + borderColor = hoverCosmetics && hoverCosmetics.borderColor, + borderThickness = hoverCosmetics && hoverCosmetics.borderThickness, + attrObj = {}, + //attach the callbacks for the click and hover interactions for the hovering element. + plotEventHandlerCallback = { + 'click': function (e){ + lib.plotEventHandler.call(this, chart, e); + }, + 'hoverIn': function (e){ + lib.plotEventHandler.call(this, chart, e, 'dataplotRollover'); + }, + 'hoverOut': function (e) { + lib.plotEventHandler.call(this, chart, e, 'dataplotRollout'); + } + }; + + if (!tracker) { + // in case the tracker element is not created. Attach the callbacks for click and hovering effects. + tracker = chartGraphics.tracker = paper.circle(0, 0, 0, chartGraphics.trackerGroup) + .attr({ + 'clip-rect': chartConfig.canvasLeft + ',' + (chartConfig.canvasTop) + ',' + + chartConfig.canvasWidth + ',' + chartConfig.canvasHeight + }) + .click(plotEventHandlerCallback.click) + .trackTooltip(true) + .hover(plotEventHandlerCallback.hoverIn, plotEventHandlerCallback.hoverOut); + } + //Attach the required information for the hovering element. + point && tracker.data('eventArgs', { + x: point.x, + y: point.y, + tooltip: point.tooltip, + link: point.link + }); + + /* store the hovered point as last visible point. This is required to avoid redaundant calls if the same + point is hovered. */ + chartConfig.lastHoveredPoint = point; + + // if hover cosmetics then add that in the attrObj + if (Number(showHover)) { + attrObj = { + r: radius, + fill: fill, + stroke: borderColor, + 'stroke-width': borderThickness + }; + } + else { // make transparent configuration + attrObj = { + r: radius, + fill: TRACKER_FILL, + stroke: TRACKER_FILL, + 'stroke-width': 0 + }; + } + tracker + .attr(attrObj) + .tooltip(toolText) + .transform('t' + (cx + chartConfig.canvasLeft) + ',' + (cy + chartConfig.canvasTop)); + /*on first mouse move the element is created and on the next mouse move the tooltip is shown. + In order give the effect of displaying the tooltip once hovered, another mouseMove event is + fired forcefully */ + point && chart.fireMouseEvent('mouseover', tracker && tracker.node, chartConfig.lastMouseEvent); + }, + + fireMouseEvent : function (eventName, domElement, mouseEventInit) { + var event; + if (!domElement || !eventName) { + return; + } + + if(!mouseEventInit){ + mouseEventInit = {}; + } + if (mouseEventInit.originalEvent) { + mouseEventInit = mouseEventInit.originalEvent; + } + // map touch event for touch devices + if (mouseEventInit.touches) { + mouseEventInit = mouseEventInit.touches[0]; + } + + + if (domElement.dispatchEvent) { + if (MouseEvent) { + //for FireFox, chrome and opera. NOT confirmed in Safari + // Creates a MouseEvent object. + event = new MouseEvent(eventName, { + bubbles: !!mouseEventInit.bubbles, + cancelable: !!mouseEventInit.cancelable, + clientX: mouseEventInit.clientX || ( mouseEventInit.pageX && (mouseEventInit.pageX - + doc.body.scrollLeft - doc.documentElement.scrollLeft)) || 0, + clientY: mouseEventInit.clientY || ( mouseEventInit.pageY && (mouseEventInit.pageY - + doc.body.scrollTop - doc.documentElement.scrollTop)) || 0, + screenX: mouseEventInit.screenX || 0, + screenY: mouseEventInit.screenY || 0, + pageX: mouseEventInit.pageX || 0, + pageY: mouseEventInit.pageY || 0 + }); + } + else if (doc.createEvent) { + //for IE support. + event = doc.createEvent('HTMLEvents'); + event.initEvent(eventName, !!mouseEventInit.bubbles, !!mouseEventInit.cancelable); + } + event.eventName = eventName; + event && domElement.dispatchEvent(event); + } + else if(doc.createEventObject && domElement.fireEvent){ + event = doc.createEventObject(); + event.eventType = eventName; + event.eventName = eventName; + //trigger the event forcefully. + domElement.fireEvent('on' + eventName, event); + } + }, + + configure: function () { + var style, + iapi = this, + jsonData = iapi.jsonData, + chartDef = jsonData.chart || {}, + colorManager = iapi.components.colorManager, + canvasBorderColor = colorManager.getColor('canvasBorderColor'), + config; + // todo code: Need to extend support for these attributes, temporarily hardcoded. + chartDef.animation = 0; + chartDef.showvalues = 0; + + chartAPI.msline.configure.call(iapi); + // The base configure fn above creates a dummy chart object if there is no chart object in json data. + config = iapi.config; + style = config.style; + // overwrite the existing chart configurations. + // Copy and prepare some configurations for zoom charts + extend2(config, { + useRoundEdges: pluckNumber (chartDef.useroundedges,0), + animation: false, + zoomType: 'x', + canvasPadding: pluckNumber(chartDef.canvaspadding, 0), + scrollColor: getFirstColor(pluck(chartDef.scrollcolor, + colorManager.getColor('altHGridColor'))), + scrollShowButtons: !!pluckNumber(chartDef.scrollshowbuttons, 1), + scrollHeight: pluckNumber(chartDef.scrollheight, 16) || 16, + scrollBarFlat: pluckNumber (chartDef.flatscrollbars, 0), + allowPinMode: pluckNumber(chartDef.allowpinmode, 1), + skipOverlapPoints: pluckNumber(chartDef.skipoverlappoints, 1), + showToolBarButtonTooltext: pluckNumber(chartDef.showtoolbarbuttontooltext, 1), + btnResetChartTooltext: pluck(chartDef.btnresetcharttooltext, 'Reset Chart'), + btnZoomOutTooltext: pluck(chartDef.btnzoomouttooltext, 'Zoom out one level'), + btnSwitchToZoomModeTooltext: pluck(chartDef.btnswitchtozoommodetooltext, + 'Switch to Zoom Mode
Select a subset of data to zoom ' + + 'into it for detailed view'), + btnSwitchToPinModeTooltext: pluck(chartDef.btnswitchtopinmodetooltext, + 'Switch to Pin Mode
Select a subset of data and compare ' + + 'with the rest of the view'), + /** + * @note pinPaneStroke related attribute parsing is unused in + * present JS ZoomLine implementation. + pinPaneStrokeWidth: pluckNumber(chartDef.pinpaneborderthickness, 1), + pinPaneStroke: convertColor(pluck(chartDef.pinpanebordercolor, + canvasBorderColor), pluckNumber(chartDef.pinpaneborderalpha, 15)), + */ + pinPaneFill: convertColor(pluck(chartDef.pinpanebgcolor, + canvasBorderColor), pluckNumber(chartDef.pinpanebgalpha, 15)), + zoomPaneFill: convertColor(pluck(chartDef.zoompanebgcolor, + '#b9d5f1'), pluckNumber(chartDef.zoompanebgalpha, 30)), + zoomPaneStroke: convertColor(pluck(chartDef.zoompanebordercolor, + '#3399ff'), pluckNumber(chartDef.zoompaneborderalpha, 80)), + showPeakData: pluckNumber(chartDef.showpeakdata, 0), + maxPeakDataLimit: pluckNumber(chartDef.maxpeakdatalimit, chartDef.maxpeaklimit, null), + minPeakDataLimit: pluckNumber(chartDef.minpeakdatalimit, chartDef.minpeaklimit, null), + crossline: { + enabled: pluckNumber(chartDef.showcrossline, 1), + line: { + 'stroke-width': pluckNumber(chartDef.crosslinethickness, 1), + 'stroke': getFirstColor(pluck(chartDef.crosslinecolor, '#000000')), + 'stroke-opacity': pluckNumber(chartDef.crosslinealpha, 20) / 100 + }, + labelEnabled: pluckNumber(chartDef.showcrosslinelabel, + chartDef.showcrossline, 1), + labelstyle: { + fontSize: toFloat(chartDef.crosslinelabelsize) ? + toFloat(chartDef.crosslinelabelsize) + PX : style.outCanfontSize, + fontFamily: pluck(chartDef.crosslinelabelfont, + style.outCanfontFamily) + }, + valueEnabled: pluckNumber(chartDef.showcrosslinevalues, + chartDef.showcrossline, 1), + valuestyle: { + fontSize: toFloat(chartDef.crosslinevaluesize) ? + toFloat(chartDef.crosslinevaluesize) + PX : style.inCanfontSize, + fontFamily: pluck(chartDef.crosslinevaluefont, + style.inCanvasStyle.fontFamily) + } + }, + useCrossline: pluckNumber(chartDef.usecrossline, 1), + tooltipSepChar: pluck(chartDef.tooltipsepchar, ', '), + showTerminalValidData: pluckNumber(chartDef.showterminalvaliddata, 0), + cdmchar: pluck(chartDef.dataseparator, PIPE), + cdm: pluckNumber(chartDef.compactdatamode, 0) + }); + }, + + getValuePixel: function (px) { + var chart = this, + chartConfig = chart.config, + info = chartConfig.viewPortConfig; + return info.ddsi + mathFloor(px / info.ppp); + }, + + __toolbar: function () { + var toolBoxAPI, + SymbolStore, + ComponentGroup, + HorizontalToolbar, + chart = this, + chartComponents = chart.components, + toolBox = chartComponents.tb = new (FusionCharts.register('component', ['toolbox', 'toolbox']))(), + toolConf = toolBox.getDefaultConfiguration(), + Symbol, + Scroller; + + toolBox.init({ + iAPI: { + chart: chart + }, + graphics: chart.graphics, + chart: chart, + components: chartComponents + }); + + toolBoxAPI = chartComponents.toolBoxAPI || toolBox.getAPIInstances(toolBox.ALIGNMENT_HORIZONTAL); + SymbolStore = toolBoxAPI.SymbolStore; + ComponentGroup = toolBoxAPI.ComponentGroup; + HorizontalToolbar = toolBoxAPI.Toolbar; + Symbol = toolBoxAPI.Symbol; + Scroller = toolBoxAPI.Scroller; + toolBox.graphics = {}; + + return { + reInit: function () { + toolBox.init({ + iAPI: { + chart: chart + }, + graphics: chart.graphics, + chart: chart, + components: chartComponents + }); + }, + addSymbol: function (symbolName, callBack, options, group) { + var symbol = new Symbol(symbolName); + options && group.setConfiguaration({ + buttons : extend2(extend2({},toolConf),options) + }); + callBack.tooltext = options.tooltip; + callBack && symbol.attachEventHandlers(callBack); + group.addSymbol(symbol); + return symbol; + }, + + addScroll: function (options, callBack) { + var scrl = new Scroller(options); + /*options && group.setConfiguaration({ + buttons : extend2(extend2({},toolConf),options) + }); + callBack.tooltext = options.tooltip;*/ + callBack && scrl.attachEventHandlers(callBack); + return scrl; + }, + + addComponentGroup: function (groupName, options) { + var group; + group = new ComponentGroup(); + group.setConfiguaration({ + group : { + fill : options ? options.fill : convertColor('EBEBEB', 0), + borderThickness : options ? pluckNumber(options.borderThickness, 0) : 0 + } + }); + return group; + }, + + addToolBox: function (componentGroups) { + var i, + toolBox = new HorizontalToolbar(); + + for (i = 0; i < componentGroups.length; i += 1) { + toolBox.addComponent(componentGroups[i]); + } + return toolBox; + }, + + setDrawingArea: function (toolBox, drawingArea) { + toolBox.drawingArea = drawingArea; + return toolBox; + }, + + draw: function (toolBoxes) { + var i, + toolBox, + drawingArea; + for (i = 0; i < toolBoxes.length; i += 1) { + toolBox = toolBoxes[i]; + drawingArea = toolBox.drawingArea; + toolBox.draw(drawingArea.x, drawingArea.y); + } + }, + + registerSymbol: function (symbolName, symbolPathFn) { + SymbolStore.register(symbolName, symbolPathFn); + }, + + getLogicalSpace: function (toolBox) { + return toolBox.getLogicalSpace(); + }, + + getNode: function (buttonGroup) { + return buttonGroup.node; + } + }; + }, + + __preDraw: function () { + var seriesItemConf, + ppp, + ppl, + visw, + visx, + yminValue, + ymin, + pinclip, + tAtt, + pingroup, + datalayer, + preZoomed, + scrollerLayer, + pinrect, + iapi = this, + iapiComponents = iapi.components, + paper = iapiComponents.paper, + iapiGraphics = iapi.graphics, + //todo: containerelement should be a part of graphics components. + // containerElem = iapiGraphics.container || iapi.container, + imageContainer = iapiGraphics.imageContainer, + iapiConfig = iapi.config, + canvasLeft = iapiConfig.canvasLeft, + canvasWidth = iapiConfig.canvasWidth, + chartDef = iapi.jsonData.chart, + cdm = iapiConfig.cdm, + xAxis = iapiComponents.xAxis[0], + // listeners = iapiComponents.eventListeners || (iapiComponents.eventListeners = []), + // containerElem = iapi.container, + info = iapiConfig.viewPortConfig, + canvasConfig = iapi.components.canvas.config, + canvasPadding = mathMax(canvasConfig.canvasPadding, canvasConfig.canvasPaddingLeft, + canvasConfig.canvasPaddingRight), + yAxis = iapiComponents.yAxis[0], + datasetGroup = iapiGraphics.datasetGroup, + canvasHeight = iapiConfig.canvasHeight, + canvasTop = iapiConfig.canvasTop, + chartAttrs = iapi.jsonData.chart, + borderWidth = iapiConfig.borderWidth || (iapiConfig.borderWidth = pluckNumber (chartAttrs.showborder, + 1) ? pluckNumber (chartAttrs.borderthickness, 1) : 0), + allowpin = iapiConfig.allowPinMode, + crosslineOptions = iapiConfig.crossline, + clen = xAxis.getCategoryLen(), + // Validate and parse the data display indices and also calculate + // initial pixels-per-point. + dsi = toInt(pluckNumber(chartDef.displaystartindex, 1), 10) - 1, + dei = toInt(pluckNumber(chartDef.displayendindex, clen || 2), 10) - 1, + + overFlowingMarkerWidth = 0, + dataSetArr = iapiComponents.dataset, + j = dataSetArr.length, + crossline = iapiGraphics.crossline; + + iapiConfig.updateAnimDuration = 500; // default animation durations. + //the origin of the container element should coincide with the origin(top-left) of the canvas area. + + imageContainer.transform('t' + canvasLeft + ',' + canvasTop); + //apply clipping to the container element. + imageContainer.attr({ + 'clip-rect': canvasLeft + ',' + canvasTop + ',' + canvasWidth + ',' + canvasHeight + }); + iapiConfig.status = 'zoom'; + //set the maximum scaleX and scaleY. + iapiConfig.maxZoomLimit = pluckNumber(chartDef.maxzoomlimit, 1000); + + //stores the different visual configurations for a historical reference. + iapiConfig.viewPortHistory = []; + + ((ppp = pluckNumber(chartDef.pixelsperpoint, 15)) < 1) && (ppp = 1); + ((ppl = pluckNumber(chartDef.pixelsperlabel, chartDef.xaxisminlabelwidth, + xAxis.getAxisConfig('labels').rotation ? 20 : 60)) < ppp) && (ppl = ppp); + // start index must be positive and less than end. last index must + // not be greater than category count. + (dsi < 0 || dsi >= ((clen - 1) || 1)) && (dsi = 0); + (dei <= dsi || dei > ((clen - 1) || 1)) && (dei = (clen - 1) || 1); + // Set initial zoom information + info = (iapiConfig.viewPortConfig = extend2(iapiConfig.viewPortConfig, { + // cnd: pluckNumber(chartDef.connectnulldata, 0), // connectNullData + amrd: pluckNumber(chartDef.anchorminrenderdistance, 20), // anchor render distance + nvl: pluckNumber(chartDef.numvisiblelabels, 0), // num visible labels + cdm: cdm, // compact data mode + oppp: ppp, // original pixels per point + oppl: ppl, // original pixels per label + dsi: dsi, // dislay start index + dei: dei, // display end index + vdl: dei - dsi, // visible display length + // dmax: legacyYAxisConf.max = dmax, // max value of all data + // dmin: legacyYAxisConf.min = dmin, // min value of all data + // dsecondarymax : secondaryYAxisConf.max = dsecondarymax, + // dsecondarymin : secondaryYAxisConf.min = dsecondarymin, + clen: clen, // category length and data length + + // Internal variables required for zoom state. + offset: 0, // (internal) drawing offset for smooth scroll, + step: 1, // (internal) default stepping or skipping, + llen: 0, // (internal) number of labels + alen: 0, // (internal) length of anchors already drawn + ddsi: dsi, // (internal) dynamic display start as per scroll + ddei: dei, // (internal) dynamic display end as per scroll + ppc: 0 // (internal) pixels per category + })); + + if (!info.clen) { + return; + } + // calculate the overflowing markers width + while (j--) { + seriesItemConf = dataSetArr[j].config; + overFlowingMarkerWidth = mathMax(overFlowingMarkerWidth, ((seriesItemConf.drawanchors && + ((seriesItemConf.anchorradius || 0) + (Number(seriesItemConf.anchorborderthickness) || 0))) || 0)); + } + + iapiConfig.overFlowingMarkerWidth = overFlowingMarkerWidth; + canvasPadding = (iapiConfig.canvasPadding = mathMax(overFlowingMarkerWidth, canvasPadding)); + + // Do a check whether user has initially zoomed. That would + // mean to keep zoomOut button visible and also to create a + // fake first zoom level. + preZoomed = iapiConfig._prezoomed = (info.dei - info.dsi < info.clen - 1); + // Set the visual dimensions of plot inside canvas. + visw = iapiConfig._visw = iapiConfig.canvasWidth - canvasPadding * 2; + visx = iapiConfig._visx = iapiConfig.canvasLeft + canvasPadding; + iapiConfig._visout = -(iapiConfig.height + canvasHeight + 1e3); + + // Get the y-axis pixel value ratio, other y-axis related values and store for later use. + iapiConfig._ypvr = yAxis && yAxis.getPVR() || 0; + yminValue = iapiConfig._yminValue = yAxis.getLimit().min; + ymin = iapiConfig._ymin = yAxis.getPixel(yminValue); + + // Clip the dataset layer to required dimension. This layer will be + // scrolled by the scroller + // Increase the clipping region to acomodate overflowing anchors + datalayer = datasetGroup.attr('clip-rect', [visx - overFlowingMarkerWidth, + canvasTop, visw + (overFlowingMarkerWidth * 2), canvasHeight]); + scrollerLayer = iapiGraphics.scroll || (iapiGraphics.scroll = + paper.group('scroll').insertAfter(iapiGraphics.datasetGroup)); + + // Create the group to store pinlines. We do it here as it is easier + // to transform that way. + if (allowpin) { + // Create the pin graphics. We create a background that sits atop the + // canvas and is clipped to match size later. + tAtt = R.crispBound(0, canvasTop - ymin, 0, canvasHeight, borderWidth); + /** @todo need to not do renderer based clipping box after raphael's + * clipping on transformed group is addressed. + */ + pinclip = iapiConfig['clip-pinrect'] = + [tAtt.x, canvasTop, tAtt.width, tAtt.height]; + pingroup = (iapiGraphics.zoompin = paper.group('zoompin')) + .insertBefore(datalayer) + .transform(iapiConfig._pingrouptransform = ['T', visx, ymin]).hide(); + + pinrect = iapiGraphics.pinrect = paper.rect(0, canvasTop - ymin, + visw, canvasHeight, pingroup).attr({ + 'stroke-width': 0, + stroke: 'none', + fill: iapiConfig.pinPaneFill, + 'shape-rendering': 'crisp', + ishot: true + }); + + // draw pin tracker + iapiGraphics.pintracker = paper.rect(iapiGraphics.trackerGroup).attr({ + transform: pingroup.transform(), + x: 0, + y: canvasTop - ymin, + width: 0, + height: canvasHeight, + stroke: 'none', + fill: TRACKER_FILL, + ishot: true, + cursor: R.svg && 'ew-resize' || 'e-resize' + }) + .hide() + .drag(function (_dx) { + var offset = visx + _dx + this.__pindragdelta, + pbl = this.__pinboundleft, + pbr = this.__pinboundright, + clip = this.data('cliprect').slice(0); + + // Restrict to boundaries. + if (offset < pbl) { + offset = pbl; + } + else if (offset > pbr) { + offset = pbr; + } + + pingroup.transform(['T', offset, ymin]); + iapiGraphics.pintracker.transform(pingroup.transform()); + // reclip the pin area visibility for VML browser + if (!R.svg) { + clip[0] = clip[0] + offset - visx - this.__pindragdelta; + pingroup.attr('clip-rect', clip); + } + this.__pindragoffset = _dx; + }, function () { + + // Calculate the pin bounds. + /** @todo remove renderer checks */ + this.__pinboundleft = 0 - pinclip[0] + visx + canvasLeft; + this.__pinboundright = this.__pinboundleft + visw - pinclip[2]; + + // Store the value of clip-rect to be used later while re-clipping + // the pin area for VML browsers, as VML browser doesn't support clipispath + + this.data('cliprect', pingroup.attr('clip-rect')); + + // temporarily mark the group as if it is a clip path and + // not a clip rect. This is to prevent raphael's internal + // matrix inversion on clipping. + pingroup._.clipispath = true; + }, function () { + pingroup._.clipispath = false; // reset clip behavior + // store the last delta movement so that dragging can be + // resumed on next drag on same pin instance. + this.__pindragdelta += this.__pindragoffset; + + // delete unneeded flags and accumulators; + delete this.__pindragoffset; + delete this.__pinboundleft; + delete this.__pinboundright; + }); + } + + // Draw the scrollbars and perform the initial configuration of + // the scrollbars. + // First crispen the scroller position to keep it in sync with the + // canvas. We temporarily increment plotborderwidth to account for + // the 1 pixel stroke width of scrollbar. + borderWidth++; + tAtt = R.crispBound(canvasLeft - borderWidth, canvasTop + canvasHeight + borderWidth, + canvasWidth + borderWidth + borderWidth, iapiConfig.scrollHeight, borderWidth); + borderWidth--; // revert the temporary increment + // During creation of the scroller element, we check for the + // "useRoundEdges" options and based on that, match the visuals and + // positioning of the scrollbar with the canvas. We shift it up + // slightly when useRoundEdges is on; so that it covers the lower + // round edges of the canvas. + + // Bind pin and zoom selection events. + bindSelectionEvent(iapi, { + attr: { + stroke: iapiConfig.zoomPaneStroke, + fill: iapiConfig.zoomPaneFill, + strokeWidth: 0 + }, + + selectionStart: function () {}, + + selectionEnd: function (o) { + // Calculate the pixel start and pixel end of the selection. + var pxs = o.selectionLeft - canvasLeft, + pxe = pxs + o.selectionWidth; + // In case chart crossline is available and visible, we hide it. + // In all cases, on selection end, the crossline is not needed + // to be visible. + iapiGraphics.crossline && iapiGraphics.crossline.hide(); + // Based on the flag status of pin mode, we send the selection + // pixels to either pinRange or zoomRange functions. + iapi[iapiConfig.viewPortConfig.pinned ? + 'pinRangePixels' : 'zoomRangePixels'](pxs, pxe); + } + }); + // Create the crosshair and its related events. The crossline + // constructor is self aware and independently configures itself + // from the chart options. + if (crosslineOptions && crosslineOptions.enabled !== 0 && iapiConfig.useCrossline === 1) { + if (!crossline) { + crossline = iapiGraphics.crossline = new CrossLine(); + } + crossline.configure(iapi, crosslineOptions); + } + else { + crosslineOptions && (crosslineOptions.enabled = 0); + crossline && crossline.hide(); + } + }, + + resetZoom: function () { + var iapi = this, + iapiConfig = iapi.config, + history = iapiConfig.viewPortHistory, + origInfo = history[0]; + + // cannot reset twice! + if (!history.length) { + return false; + } + + + history.length = 0; // clear history + if (iapi.zoomTo(origInfo.dsi, origInfo.dei, origInfo)) { + /** + * This event is fired whenever the zoom history is cleared on a ZoomLine chart. + * + * @event FusionCharts#zoomReset + * @group chart-zoomline:zoom + */ + global.raiseEvent('zoomReset', iapi._zoomargs, + iapi.chartInstance, [iapi.chartInstance.id]); + } + + return true; + }, + + eiMethods: /** @lends FusionCharts# */ { + /** + * Zooms ZoomLine chart one level out + * @group chart-zoomline:zoom-1 + * + * @fires FusionCharts#zoomed + * @fires FusionCharts#zoomedOut + */ + zoomOut: function (callback) { + var chart = this.apiInstance, + asyncRender = chart.chartInstance.args.asyncRender, + output, + list = chart.getJobList(); + + if (!chart) { + return; + } + + if (asyncRender) { + list.eiMethods.push(schedular.addJob(function () { + output = chart.zoomOut && chart.zoomOut(); + if (typeof callback === 'function') { + callback(output); + } + }, chart, [], priorityList.postRender)); + } + else { + return chart.zoomOut && chart.zoomOut(); + } + }, + + scrollTo: chartAPI.mscartesian._scrollTo, + /** + * Zooms ZoomLine chart to a range of data. + * + * @group chart-zoomline:zoom-2 + * + * @fires FusionCharts#zoomed + * @fires FusionCharts#zoomedIn + * + * @param {number} startIndex The index of the dataset from which it needs to be zoomed into. + * @param {number} endIndex the index of the dataset until which it needs to be zoomed into. + */ + zoomTo: function (startIndex, endIndex, callback) { + var chart = this.apiInstance, + asyncRender = chart.chartInstance.args.asyncRender, + output, + list = chart.getJobList(); + + if (!chart) { + return; + } + + if (asyncRender || callback) { + list.eiMethods.push(schedular.addJob(function () { + output = chart.zoomRange && chart.zoomRange(startIndex, endIndex); + if (typeof callback === 'function') { + callback(output); + } + }, chart, [], priorityList.postRender)); + } + else { + return chart.zoomRange && chart.zoomRange(startIndex, endIndex); + } + }, + + /** + * Reset all zoom, pan and pin actions that has been done on ZoomLine chart. + * + * @group chart-zoomline:zoom-3 + * + * @fires FusionCharts#zoomReset + */ + resetChart: function () { + var chart = this.apiInstance, + asyncRender = chart.chartInstance.args.asyncRender, + list = chart.getJobList(), + _helperFn = function () { + chart.pinRangePixels && chart.pinRangePixels(); + chart.resetZoom && chart.resetZoom(); + }; + + if (!chart) { + return; + } + + if (asyncRender) { + list.eiMethods.push(schedular.addJob(_helperFn, chart, [], priorityList.postRender)); + } + else { + _helperFn(); + } + }, + + /** + * Switches between zoom and pin mode. This function does not work when `allowPinMode` is set to `0` in + * chart XML or JSON. + * + * Zoom Line charts can have either a zoom mode or a pin mode. Zoom mode lets you select a section of the + * chart by dragging mouse cursor across the canvas and the chart zooms in on the selected section. In pin + * mode, the selected portion can be dragged around to compare with the rest of the chart. Zoom mode and pin + * mode can be toggled by clicking a button on the top right corner of the chart. This function lets you + * switch between zoom mode and pin mode programmatically. + * + * @group chart-zoomline:zoom-4 + * + * @fires FusionCharts#zoomModeChanged + * + * @param {boolean} yes Boolean value to be `true` if zoom mode needs to be activated, `false` to activate + * pin mode. + */ + setZoomMode: function (yes) { + var chart = this.apiInstance, + asyncRender = chart.chartInstance.args.asyncRender, + list = chart.getJobList(); + + if (!chart) { + return; + } + + if (asyncRender) { + list.eiMethods.push(schedular.addJob(function () { + chart.activatePin && chart.activatePin(!yes); + }, chart, [], priorityList.postRender)); + } + else { + chart.activatePin && chart.activatePin(!yes); + } + }, + + /** + * Returns the index of the first visible point on canvas of ZoomLine chart + * @group chart-zoomline:view-1 + * @returns {number} + */ + getViewStartIndex: function (callback) { + var chart = this.apiInstance, + asyncRender = chart.chartInstance.args.asyncRender, + zi, + list = chart.getJobList(); + + if (callback || asyncRender) { + list.eiMethods.push(schedular.addJob(function () { + if (typeof callback === 'function') { + if (!(chart && (zi = chart.config.viewPortConfig))) { + return; + } + callback(zi.ddsi); + } + }, chart, [], priorityList.postRender)); + } + else { + if (!(chart && (zi = chart.config.viewPortConfig))) { + return; + } + return zi.ddsi; + } + }, + + /** + * Returns the index of the last visible point on canvas of ZoomLine chart + * @group chart-zoomline:view-2 + * @returns {number} + */ + getViewEndIndex: function (callback) { + var chart = this.apiInstance, + asyncRender = chart.chartInstance.args.asyncRender, + zi, + vei, + list = chart.getJobList(); + + + if (callback || asyncRender) { + list.eiMethods.push(schedular.addJob(function () { + if (typeof callback === 'function') { + if (!(chart && (zi = chart.config.viewPortConfig))) { + return; + } + + vei = zi.ddei - 1; + callback((vei >= zi.clen ? zi.clen : vei) - 1); + } + }, chart, [], priorityList.postRender)); + } + else { + if (!(chart && (zi = chart.config.viewPortConfig))) { + return; + } + + vei = zi.ddei - 1; + return ((vei >= zi.clen ? zi.clen : vei) - 1); + } + } + }, + + zoomOut: function () { + var lastinfo, + origInfo, + iapi = this, + iapiConfig = iapi.config, + history = iapiConfig.viewPortHistory, + dsi, + dei, + args; + lastinfo = history.pop(); // access the last history + origInfo = history[0] || iapiConfig.viewPortConfig; + if (lastinfo) { + dsi = lastinfo.dsi; + dei = lastinfo.dei; + } + // If zoom level is less than 1, it is equivalent to reset. + else { + // But, in case chart was initially zoomed, we need to zoom out + // to full view. + if (iapiConfig._prezoomed) { + dsi = 0; + dei = origInfo.clen - 1; + } + } + /*info.lskip = lastinfo.lskip; + info.step = lastinfo.step; + iapi.components.xAxis[0].setAxisConfig({ + 'labelStep': info.lskip + 1 + });*/ + if (args = iapi.zoomTo(dsi, dei, lastinfo)) { + /** + * This event is fired when user zooms out on a ZoomLine chart. + * + * @event FusionCharts#zoomedOut + * @group chart-zoomline:zoom + * + * @param {number} level - Indicates to which zoom level the user has zoomed out to. `1` indicates that + * the chart has been completely zoomed out. + * + * @param {number} startIndex - The data start index that is in view for the zoomed out level + * @param {string} startLabel - The label of the data of the starting item in view. + * @param {number} endIndex - The data end index that is in view for the zoomed out level + * @param {string} endLabel - The label of the data of the last item in view. + */ + global.raiseEvent('zoomedout', args, iapi.chartInstance); + } + + return true; + }, + + zoomRangePixels: function (pxs, pxe) { + var chart = this, + chartConfig = chart.config, + history = chartConfig.viewPortHistory, + info = chartConfig.viewPortConfig, + ppp = info.ppp, + start = info.ddsi, + args; + history.push(info); // push current state to history + + // Peform function equvalent to this.getValuePixel(). + // Code repeated here for lesser performance penalty of function + // call. + /*if (args = chart.zoomTo(start + mathFloor(pxs / ppp), + start + mathFloor(pxe / ppp))) {*/ + if (args = chart.zoomTo(start + mathFloor(pxs / ppp), + start + mathFloor(pxe / ppp))) { + /** + * This event is fired when user zooms in on a ZoomLine chart. + * + * @event FusionCharts#zoomedIn + * @group chart-zoomline:zoom + * + * @param {number} level - Indicates to which zoom level the user has zoomed out to. `1` indicates that + * the chart has been completely zoomed out. It increments as user zooms in further. + * + * @param {number} startIndex - The data start index that is in view for the zoomed in level + * @param {string} startLabel - The label of the data of the starting item in view. + * @param {number} endIndex - The data end index that is in view for the zoomed in level + * @param {string} endLabel - The label of the data of the last item in view. + */ + global.raiseEvent('zoomedin', args, chart.chartInstance); + } + else { + // If zooming has failed then pop the history state that we + // pushed earlier. + history.pop(); + } + + }, + + zoomRange: function (dsi, dei) { + var pxs, + pxe, + iapi = this, + iapiConfig = iapi.config, + info = iapiConfig.viewPortConfig, + lastinfo, + xAxis = iapi.components.xAxis[0], + history = iapiConfig.viewPortHistory, + args; + + history.push(info); // push current state to history + lastinfo = info; + info = history[0]; + pxs = xAxis.getPixel(dsi); + pxe = xAxis.getPixel(dei); + lastinfo.x = pxs; + lastinfo.scaleX = iapiConfig.canvasWidth / (pxs - pxe); + info = lastinfo; + if (args = iapi.zoomTo(+dsi, +dei)) { + // already described in apidocs insidr zoomRangePixls + global.raiseEvent('zoomedin', args, iapi.chartInstance); + } + else { + // If zooming has failed then pop the history state that we + // pushed earlier. + history.pop(); + } + }, + + zoomTo: function (dsi, dei, lastinfo) { + var pxs, + pxe, + origPos, + chart = this, + chartConfig = chart.config, + components = chart.components, + // labels = chart.xlabels.data, + info = chartConfig.viewPortConfig, + canvasHeight = chartConfig.canvasHeight, + canvasLeft = chartConfig.canvasLeft, + canvasTop = chartConfig.canvasTop, + canvasBottom = chartConfig.canvasBottom, + newinfo, + history = chartConfig.viewPortHistory, + clen = info.clen, + args, + xAxis = chart.components.xAxis[0]; + // Detect max zoom and update the zoom history. + // start index must be positive and less than end. last index must + // not be greater than category count. + (dsi < 0) && (dsi = 0); + (dsi >= clen - 1) && (dsi = clen - 1); + (dei <= dsi) && (dei = dsi + 1); + (dei > clen - 1) && (dei = clen - 1); + // Find the final zoom level and bail out. + if (dsi === dei || (dsi === info.dsi && dei === info.dei)) { + return false; + } + + // Revert pin mode, in case it is active. + chart.pinRangePixels(); + // dsi = Math.floor(xAxis.getValue(pxs)); + // dei = Math.ceil(xAxis.getValue(pxe)); + newinfo = extend2({}, info); + newinfo.dsi = dsi; + newinfo.dei = dei; + info = (chartConfig.viewPortConfig = newinfo); // set current state + // Derive new zoom state from existing state. + if (!lastinfo) { + pxs = xAxis.getPixel(dsi); + pxe = xAxis.getPixel(dei); + origPos = chart.getOriginalPositions(pxs - canvasLeft, + canvasTop, pxe - canvasLeft, canvasBottom - canvasTop); + chart.zoomSelection(origPos[0], 0, origPos[2], canvasHeight); + } + else { + chart.updateVisual(lastinfo.x, lastinfo.y, lastinfo.scaleX, lastinfo.scaleY); + } + components.scrollBar.node.attr({ + 'scroll-ratio': info.vdl / (clen - !!clen), + 'scroll-position': [info.dsi / (clen - info.vdl - 1), true] + }); + args = { + level: history.length + 1, + startIndex: dsi, + startLabel: xAxis.getLabel(dsi).label, + endIndex: dei, + endLabel: xAxis.getLabel(dei).label + }; + + /** + * This event is fired when user either zooms in or zooms out on a ZoomLine chart. + * + * @event FusionCharts#zoomed + * @group chart-zoomline:zoom + * + * @param {number} level - Indicates to which zoom level the user has zoomed to. `1` indicates that + * the chart has been completely zoomed out. It increments as user zooms in further and decrements when user + * zooms out. + * + * @param {number} startIndex - The data start index that is in view for the zoomed level + * @param {string} startLabel - The label of the data of the starting item in view. + * @param {number} endIndex - The data end index that is in view for the zoomed level + * @param {string} endLabel - The label of the data of the last item in view. + */ + global.raiseEvent('zoomed', args, chart.chartInstance, [chart.chartInstance.id, dsi, dei, args.startLabel, + args.endLabel, args.level]); + + return args; + }, + + activatePin: function (yes) { + var iapi = this, + iapiConfig = iapi.config, + toolBoxGraphics = iapi.components.tb.graphics, + info = iapiConfig.viewPortConfig, + button = toolBoxGraphics.pinButton; + + // Checking for button is an indirect way to check if pinning is + // allowed. + /*if (!button) { + return; + }*/ + + // If pin is already active and "yes" is true then we get lost. + if (!(info.pinned ^ (yes = !!yes))) { + return; + } + if (!yes) { + // Call pin range with no range to deactivate pin. + iapi.pinRangePixels(); + } + + /** + * This event is fired when user toggles between zoom and pin mode of a zoomline chart. + * + * @event FusionCharts#zoomModeChanged + * @group chart-zoomline:zoom + * + * @param {boolean} pinModeActive - `true` indicates that post the mode change, pin mode is active. + */ + global.raiseEvent('zoomModeChanged', { + pinModeActive: yes + }, iapi.chartInstance, []); + /*iapiConfig.showToolBarButtonTooltext && + (button.conf.tooltip = iapiConfig[yes && + 'btnSwitchToZoomModeTooltext' || + 'btnSwitchToPinModeTooltext'] || BLANK);*/ + iapi.updateButtonVisual(button.node, yes ? 'pressed' : 'enable'); + + return (info.pinned = yes); + }, + + updateButtonVisual: function (buttonNode, mode) { + var config = { + disable:{ + config: { + hover: { + fill: COLOR_WHITE, + 'fill-symbol': COLOR_WHITE, + 'stroke-width': 1, + stroke: COLOR_E3E3E3, + cursor: STR_DEF + }, + normal: { + fill: COLOR_WHITE, + 'fill-symbol': COLOR_WHITE, + stroke: COLOR_E3E3E3, + 'stroke-width': 1, + cursor: STR_DEF + }, + disable: { + fill: COLOR_WHITE, + 'fill-symbol': COLOR_WHITE, + 'stroke-width': 1, + stroke: COLOR_E3E3E3, + 'stroke-opacity': 1, + cursor: STR_DEF + }, + pressed: { + fill: COLOR_WHITE, + 'fill-symbol': COLOR_WHITE, + 'stroke-width': 1, + stroke: COLOR_E3E3E3, + cursor: STR_DEF + } + }, + 'button-disabled': false, + 'stroke': COLOR_E3E3E3, + 'stroke-opacity': 1 + } , + enable: { + config: { + hover: { + fill: COLOR_WHITE, + 'fill-symbol': COLOR_WHITE, + 'stroke-width': 1, + stroke: '#aaaaaa', + cursor: 'pointer' + }, + normal: { + fill: COLOR_WHITE, + 'fill-symbol': COLOR_WHITE, + stroke: COLOR_C2C2C2, + 'stroke-width': 1, + cursor: 'pointer' + }, + disable: { + fill: COLOR_WHITE, + 'fill-symbol': COLOR_WHITE, + 'stroke-width': 1, + stroke: COLOR_E3E3E3, + 'stroke-opacity': 1, + cursor: 'pointer' + }, + pressed: { + fill: COLOR_EFEFEF, + 'fill-symbol': COLOR_EFEFEF, + 'stroke-width': 1, + stroke: COLOR_C2C2C2, + cursor: 'pointer' + } + }, + 'button-disabled': false, + fill: [COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, true], + 'stroke': COLOR_C2C2C2, + 'stroke-opacity': 1 + }, + pressed: { + config: { + hover: { + fill: '#dcdcdc', + 'fill-symbol': COLOR_WHITE, + 'stroke-width': 1, + stroke: '#b7b7b7', + cursor: 'pointer' + }, + normal: { + fill: '#dcdcdc', + 'fill-symbol': COLOR_WHITE, + 'stroke-width': 1, + stroke: '#b7b7b7', + cursor: 'pointer' + }, + pressed: { + fill: '#dcdcdc', + 'fill-symbol': COLOR_WHITE, + 'stroke-width': 1, + stroke: '#b7b7b7', + cursor: 'pointer' + } + }, + fill: '#dcdcdc', + 'fill-symbol': COLOR_WHITE, + 'stroke-width': 1, + stroke: '#b7b7b7', + cursor: 'pointer' + } + }; + return buttonNode.attr(config[mode]); + }, + + pinRangePixels: function (pxs, pxe) { + var plotGraphics, + iapi = this, + iapiComponents = iapi.components, + paper = iapiComponents.paper, + iapiGraphics = iapi.graphics, + iapiConfig = iapi.config, + canvasLeft = iapiConfig.canvasLeft, + // elements = chart.elements, + // labels = chart.xlabels.data, + info = iapiConfig.viewPortConfig, + pingroup = iapiGraphics.zoompin, + pinrect = iapiGraphics.pinrect, + pinclip = iapiConfig['clip-pinrect'], + pingrouptransform = iapiConfig._pingrouptransform, + plots = iapiComponents.dataset, + pxw = pxe - pxs, + plot, + pinline, + i, + pintracker = iapiGraphics.pintracker; + // Find the no-pin situation and bailout. + if (!info || !pingroup || !pinrect) { + return; + } + // Hide + if (pxs === pxe) { + pingroup.hide(); + pintracker.hide(); + // todo code + // chart.pinButton.attr('button-active', false); + return info.pinned = false; + } + + // Iterate over plots and copy the lines. + i = plots.length; + while (i--) { + plot = plots[i]; + plotGraphics = plot.graphics; + pinline = plotGraphics.pinline; + if (!pinline) { + pinline = plotGraphics.pinline = paper.path(pingroup); + } + pinline.attr({ + 'path': plotGraphics.lineElement.attrs.path, + 'transform': ['T',-iapiConfig._visx, -iapiConfig._ymin] + }) + .attr(plot.config.pin); + } + + // Adjust the cliprect to the new x amd width. + /** @todo remove renderer checks */ + pinclip[0] = pxs + canvasLeft; + pinclip[2] = pxw; + pingroup.attr({ + 'clip-rect': pinclip, + 'transform': pingrouptransform + }).show(); + pintracker.__pindragdelta = 0; // dragging helper + pintracker.show().attr({ + transform: pingrouptransform, + x: pxs, + width: pxw + }); + + // store the dsi and dei in px variables for raising in events + pxs = iapi.getValuePixel(pxs); + pxe = iapi.getValuePixel(pxe); + /** + * This event is fired when user switches to pin mode on zoomline chart and then performs a selection on the + * data plot to "pin" a range. + * + * @event FusionCharts#pinned + * + * @group chart-zoomline + * + * @param {number} startIndex - The data start index of the pinned range. + * @param {string} startLabel - The label of the data of the starting item of the pinned range. + * @param {number} endIndex - The data end index that is in view of the pinned range. + * @param {string} endLabel - The label of the data of the last item of the pinned range. + */ + // global.raiseEvent('pinned', { + // startIndex: pxs, + // endIndex: pxe, + // startLabel: labels[pxs], + // endLabel: labels[pxe] + // }, iapi.chartInstance, [iapi.chartInstance.id, pxs, pxe, + // labels[pxs], labels[pxe]]); + + // state pin state in meta. + return info.pinned = true; + }, + + _createLayers: function () { + var iapi = this, + iapiGraphics, + paper = iapi.components.paper; + chartAPI.scatter._createLayers.call(iapi); + iapiGraphics = iapi.graphics; + //create the additional layers. + //create the container element + iapiGraphics.imageContainer = paper.group('dataset-orphan', iapiGraphics.dataSetGroup); + iapi.__preDraw(); + iapi.toogleDragPan(true); + }, + + getValue: function (point) { + var chart = this, + chartConfig = chart.config, + chartComponents = chart.components, + viewPortConfig = chartConfig.viewPortConfig, + //the pixel wrt original canvas size + origpixel = chart.getOriginalPositions(point.x,point.y,point.x,point.y), + origX = origpixel[0], + origY = origpixel[1], + xAxis = chartComponents.xAxis[0], + yAxis = chartComponents.yAxis[0], + xaxisRange = xAxis.config.axisRange, + yaxisRange = yAxis.config.axisRange, + minX = xaxisRange.min, + maxX = xaxisRange.max, + maxY = yaxisRange.max, + minY = yaxisRange.min, + //calcualte the Pixel to Value Ratios. + xPVR = chartConfig.canvasWidth * viewPortConfig.scaleX / (maxX - minX), + yPVR = chartConfig.canvasHeight * viewPortConfig.scaleY / (maxY - minY); + + return { + x: minX + ((origX - chartConfig.canvasLeft) / xPVR), + y: maxY - ((origY - chartConfig.canvasTop) / yPVR) + }; + }, + + getOriginalPositions: function (x1,y1,x2,y2) { + var newW, + newH, + newX, + newY, + chart = this, + chartConfig = chart.config, + viewPortConfig = chartConfig.viewPortConfig, + oldScaleX = viewPortConfig.scaleX, + oldScaleY = viewPortConfig.scaleY, + //coodinates of the visual canvas origin wrt to original canvas. + oldX = viewPortConfig.x, + oldY = viewPortConfig.y, + + xMin = mathMin(x1, x2), + xMax = mathMax(x1, x2), + yMin = mathMin(y1, y2), + yMax = mathMax(y1, y2); + + //Right Bottom limit boundary + xMax = xMax > chartConfig.canvasWidth ? chartConfig.canvasWidth : xMax; + yMax = yMax > chartConfig.canvasHeight ? chartConfig.canvasHeight : yMax; + //Left Top Limit Boundary + xMin = xMin < 0 ? 0 : xMin; + yMin = yMin < 0 ? 0 : yMin; + // update the dimensions wrt to initial viewPort configurations. + newW = (xMax - xMin ) / oldScaleX; + newH = (yMax - yMin ) / oldScaleY; + newX = oldX + (xMin / oldScaleX); + newY = oldY + (yMin / oldScaleY); + //converts to the coordinates wrt original image + return [newX, newY, newW, newH]; + }, + + zoomSelection: function (x, y, w, h) { + var chart = this, + chartConfig = chart.config, + scaleX, + scaleY, + newWidth, + newHeight, + maxX, + maxY, + newOriginX, + newOriginY; + + + // if the width or height is 0 return it + if (!w || !h) { + return; + } + //scale factors cannnot be negatives. + scaleX = Math.abs(chartConfig.canvasWidth / w); + scaleY = Math.abs(chartConfig.canvasHeight / h); + //total dimensions it would look alike when zoomed with these scale factors + newWidth = chartConfig.canvasWidth * scaleX; + newHeight = chartConfig.canvasHeight * scaleY; + //the amount to be shifted so that the zoom portion falls in the visible area. + newOriginX = x * scaleX; + newOriginY = y * scaleY; + //impose restrictions on the boundaries. + maxX = (newWidth - chartConfig.canvasWidth); + maxY = (newHeight - chartConfig.canvasHeight); + + //left top restricition + newOriginX = (newOriginX < 0) ? 0 : newOriginX; + newOriginY = (newOriginY < 0) ? 0 : newOriginY; + + //right bottom restriction + newOriginX = (newOriginX > maxX) ? maxX : newOriginX; + newOriginY = (newOriginY > maxY) ? maxY : newOriginY; + //update the final visual(drawing) part. + chart.updateVisual(x, y, scaleX, scaleY); + }, + + updateVisual: function (zoomX, zoomY, scaleX, scaleY, pixelatedDraw) { + var chart = this, + chartConfig = chart.config, + viewPortConfig = chartConfig.viewPortConfig, + oldCanvasWidth = chartConfig.canvasWidth, + oldCanvasHeight = chartConfig.canvasHeight, + // newCanvasWidth = oldCanvasWidth, + // newCanvasHeight = oldCanvasHeight, + viewPortHistory = chartConfig.viewPortHistory, + lastViewPortConfig = viewPortHistory.slice(-1)[0] || viewPortConfig, + // zoomEvent = [], + // viewPortStatus = viewPortConfig.status, + + // xAxis = chartComponents.xAxis[0], + // yAxis = chartComponents.yAxis[0], + + maxZoomLimit = chartConfig.maxZoomLimit; //restrictions in the zooming limit. + //check for validity for the input arguments provided. + //Incase invalid, revert to the last viewPort configurations, that is visually there remains no change. + viewPortConfig.x = isNaN(zoomX) ? (zoomX = lastViewPortConfig.x) : zoomX; + viewPortConfig.y = isNaN(zoomY) ? (zoomY = lastViewPortConfig.y) : zoomY; + viewPortConfig.scaleX = scaleX || (scaleX = lastViewPortConfig.scaleX); + viewPortConfig.scaleY = scaleY || (scaleY = lastViewPortConfig.scaleY); + + // apply the limit + if (scaleX > maxZoomLimit) { + viewPortConfig.x = zoomX = mathMin(zoomX, (oldCanvasWidth - oldCanvasWidth/maxZoomLimit)); + viewPortConfig.scaleX = scaleX = maxZoomLimit; + } + if (scaleY > maxZoomLimit) { + viewPortConfig.y = zoomY = mathMin(zoomY, (oldCanvasHeight - oldCanvasHeight/maxZoomLimit)); + viewPortConfig.scaleY = scaleY = maxZoomLimit; + } + chart.updateManager(pixelatedDraw); + }, + + toogleDragPan: function (isToogle) { + var chart = this, + chartConfig = chart.config, + viewPortConfig = chartConfig.viewPortConfig, + status = viewPortConfig.status; + if (isToogle) { + viewPortConfig.status = (status === 'zoom') ? 'pan': 'zoom'; + // global.raiseEvent('zoomModeChanged', { + // panModeActive: chartConfig.viewPortConfig.status + // }, chart.fusionCharts, [chart.fusionCharts.id]); + } + //visual update + //chart.updateButtonVisual(); + + }, + + resize: function () { + var chart = this, + chartConfig = chart.config, + chartGraphics = chart.graphics, + canvasComponents = chart.components.canvas, + canvasConfig = canvasComponents.config, + canvasGraphics = canvasComponents.graphics, + canvasBorder = canvasGraphics.canvasBorderElement, + canvas = canvasGraphics.canvasElement, + //bridge code + canvasBorderThickness = canvasConfig.canvasBorderThickness, + canvasHalfThickness = canvasBorderThickness / 2, + newHeight = (chartConfig.canvasHeight -= canvasBorderThickness), + newWidth = (chartConfig.canvasWidth -= (2*canvasBorderThickness)), + left = (chartConfig.canvasLeft += canvasBorderThickness); + chartConfig.canvasBottom -= canvasBorderThickness; + chartConfig.canvasRight -= canvasBorderThickness; + //resize the canvas according to the latest modifications in the canvas dimensions. + if (canvas) { + canvas.attr({ + x: left, + y: chartConfig.canvasTop, + height: newHeight, + width: newWidth + }); + } + else { + chart.drawCanvas(); // draw the canvas if it ceases to exist. + } + + if (canvasBorder) { + //update the dimensions of the border of the canvas + canvasBorder.attr({ + x: left - canvasHalfThickness, + y: chartConfig.canvasTop - canvasHalfThickness, + height: newHeight + canvasBorderThickness, + width: newWidth + canvasBorderThickness, + 'stroke-width': canvasBorderThickness /* stroke-width is a property being applied both on + outer and inner side, hence canvasHalfThickness is used instead the full of it.*/ + }); + } + + //update the image container wrt modified canvas dimension after resizing. + chartGraphics.imageContainer.attr({ + 'clip-rect': chartConfig.canvasLeft + ',' + chartConfig.canvasTop + ',' + chartConfig.canvasWidth + + ',' + chartConfig.canvasHeight + }).transform('t' + chartConfig.canvasLeft + ',' + chartConfig.canvasTop); + //clipping the hot tracker element. + chartGraphics.trackerElem.attr({ + x: chartConfig.canvasLeft, + y: chartConfig.canvasTop, + width: chartConfig.canvasWidth, + height: chartConfig.canvasHeight + }); + /*Clipping on the tracker is required when a single plot covers more than the screen and one hover on + it, the hovering circle goes out of the canvas, which needs to be prevented and hence needs to be + clipped. This clipping again needs to be updated with every resizing.*/ + chartGraphics.tracker && chartGraphics.tracker.attr({ + 'clip-rect': chartConfig.canvasLeft + ',' + chartConfig.canvasTop + ',' + chartConfig.canvasWidth + + ',' + chartConfig.canvasHeight + }); + }, + + updateManager: function (pos) { + var i, + labelStep, + stepValue, + chart = this, + scaleX, + chartComponents = chart.components, + dataSets = chartComponents.dataset, + len = dataSets.length, + chartConfig = chart.config, + allowPinMode = chartConfig.allowPinMode, + info = chartConfig.viewPortConfig, + ypvr = chartConfig._ypvr, + visW = chartConfig._visw, + xAxis = chart.components.xAxis[0], + getPixelX = function() { + return xAxis.getPixel.apply(xAxis,arguments); + }, + // labels = chart.xlabels, + cssLabel = xAxis.getAxisConfig('labels').style, + // labelGroup = labels.group, + // textDirection = chartConfig.textDirection, + oppp, // target pixels-per-point + vdl, // visible display length + ppl, // num visible labels + ppp, // current pixels-per-point + step, // stepping on vdl to reach target ppp + lskip, + norm, // normalizer of vdl to allow smooth scrolling + dsi, // display start index + dei, // display end index + ddsi, // dynamic dsi post normalization + ddei, + nvl, + xAxisAnifConfig, + xAxisNameDrawConfig, + visibleExtremes, + updateButtonVisual = chart.updateButtonVisual, + toolBoxGraphics = chartComponents.tb.graphics, + zoomOutButton = toolBoxGraphics.zoomOutButton, + resetButton = toolBoxGraphics.resetButton, + pinButton = toolBoxGraphics.pinButton, + history = chartConfig.viewPortHistory; + if (chartConfig.legendClicked) { + for (i = 0; i < len; i += 1) { + dataSets[i].draw(); + } + return; + } + // Use default config if none has been provided else extend current + // state. + !info && (info = chartConfig.viewPortConfig); + + // Calculate stepping values here. This is required so that the + // number of anchors can be recalculated prior to updating plot. + oppp = info.oppp; + nvl = ppl = info.nvl; + dsi = info.dsi; + dei = info.dei; + vdl = info.vdl = dei - dsi; + ppl = info.ppl = nvl ? visW / nvl : info.oppl; + // Calculate label and anchor stepping. + step = info.step = ((ppp = info.ppp = visW / vdl) < oppp) ? mathCeil(oppp / ppp) : 1; + lskip = info.lskip = mathCeil(mathMax(ppl, toFloat(cssLabel.lineHeight)) / ppp / step); + + // If scroll position is provided, we recalculate indices and + // ignore what has been sent via zoom info. + // We do not put position calculation in a separate function to + // avoid repeated recalculations + if (pos !== UNDEF) { + ddsi = (info.clen - vdl - 1) * pos ; + info.offset = (ddsi - (ddsi = toInt(ddsi))) * ppp; + ddei = ddsi + vdl; + } + else { + ddsi = info.dsi; + ddei = info.dei; + info.offset = 0; + } + norm = info.norm = ddsi % step; + + // normalize the indices + info.ddsi = (ddsi = ddsi - norm); + info.ddei = (ddei = ddei + 2 * step - norm); + info.pvr = ypvr; + info._ymin = chartConfig._ymin; + info._yminValue = chartConfig._yminValue; + info.x = ((getPixelX(ddsi) - getPixelX(xAxis.getLimit().min) + info.offset)/ info.scaleX); + // once the visible labels are exceeding the category length. + if ((ddei - ddsi) > xAxis.getCategoryLen()) { + info.scaleX = 1; + } + else { + // info.scaleX = chartConfig.canvasWidth / (mathRound((ddei - ddsi + 1)/ (step)) * 0.5 * ppl); + info.scaleX = xAxis.getCategoryLen() / Math.abs(ddei - ddsi - step - 0.9); + } + if (pos !== UNDEF){ + chartComponents.scrollBar.node && chartComponents.scrollBar.node.attr({ + 'scroll-position': (info._pos = pos) + }); + } + visibleExtremes = xAxis._getVisibleConfig(); + + stepValue = Math.ceil((visibleExtremes.maxValue - visibleExtremes.minValue + 1) / nvl); + scaleX = chartConfig.viewPortConfig && chartConfig.viewPortConfig.scaleX; + labelStep = Math.max(Math.round(xAxis.getAxisConfig('labelStep')/scaleX), nvl ? stepValue : lskip * step); + xAxis.setLabelConfig({ + step: labelStep + }); + /*xAxis.setAxisConfig({ + 'labelStep': labelStep + });*/ + xAxisAnifConfig = xAxis.getAxisConfig('animateAxis'); + xAxisNameDrawConfig = xAxis.getAxisConfig('drawAxisName'); + pos && (xAxis.setAxisConfig({ + animateAxis : false, + drawAxisName : false + })); + xAxis.draw(); + xAxis.setAxisConfig({ + animateAxis : xAxisAnifConfig, + drawAxisName : xAxisNameDrawConfig + }); + // Calculate the label length + /*len = labels.show ? mathCeil((ddei - ddsi) / step / lskip) : 0; + oldlen = info.llen - 1; + info.llen = len; + ppc = info.ppc = ppp * lskip * step; // pixels between two x-labels + labelGroup.trackTooltip(true); + // Iterate over the labels and update them. + // Add new anchor elements or if added, show them. + if (len > oldlen) { + for (j = oldlen, jj = len; j < jj; j++) { + (label = labels[j]) && label.show() || + (labels[j] = paper.text(0, 0, BLANK, labelGroup).css(cssLabel).attr({ + direction: textDirection + })); + } + } + // Hide extra label elements. + else { + for (j = len, jj = oldlen + 1; j < jj; j++) { + labels[j].hide(); + } + } + // Calculate amount of anchors that will be visible. Until amrd is reached, + // all labels should be hidden. (re-use the previous len variables) + len = (ppp * step < info.amrd) ? 0 : mathCeil((ddei - ddsi) / step); + deltalen = len - info.alen; + oldlen = info.alen - 1; + info.alen = len; // Update anchor length.*/ + // redraw the datasets + for (i = 0; i < len; i += 1) { + dataSets[i].draw(); + } + + // Show reset button when zoom history is long enough and always show + // zoom out button on every zoom-in. + updateButtonVisual(zoomOutButton.node, (info.vdl === info.clen - 1 ? 'disable' : 'enable')); + updateButtonVisual(resetButton.node, (history.length > 0 ? 'enable' : 'disable')); + updateButtonVisual(pinButton.node, (allowPinMode ? 'enable' : 'disable')); + + + if (win.FC_DEV_ENVIRONMENT && win.jQuery) { + if (FusionCharts['debugger'].enable()) { + this.debug = this.debug || (win.jQuery('#fc-zoominfo').length || + win.jQuery('body').append('
'), win.jQuery('#fc-zoominfo').css({
+                        position: 'absolute',
+                        left: '10px',
+                        top: '0',
+                        'pointer-events': 'none',
+                        opacity: 0.7,
+                        width: '250px',
+                        zIndex: '999',
+                        border: '1px solid #cccccc',
+                        'box-shadow': '1px 1px 3px #cccccc',
+                        background: '#ffffff'
+                    }));
+                    this.debug.text(JSON.stringify(info, 0, 2));
+                }
+                else {
+                    this.debug && win.jQuery('#fc-zoominfo').remove();
+                    delete this.debug;
+                }
+            }
+        },
+        _drawDataset: function () {
+            var iapi = this;
+            chartAPI.zoomline.updateManager.call(iapi);
+        },
+
+        getParsedLabel: function (index) {
+            var xlabels = this.xlabels;
+            return xlabels.parsed[index] ||
+                (xlabels.parsed[index] =
+                    parseUnsafeString(xlabels.data[index] || BLANK));
+        },
+        _createToolBox: function () {
+            var toolBox,
+                toolBoxGraphics,
+                toolBoxAPI,
+                group,
+                SymbolStore,
+                Symbol,
+                zoomOutButton,
+                resetButton,
+                pinButton,
+                iapi = this,
+                config = iapi.config,
+                allowPinMode = config.allowPinMode,
+                components = iapi.components,
+                tooltip = config.showToolBarButtonTooltext,
+                chartMenuBar = components.chartMenuBar,
+                actionBar = components.actionBar;
+            /* Do not reconfigure the toolbox if its already drawn. This flag is set falsy on each time configurations
+            are updated. */
+            if (chartMenuBar && chartMenuBar.drawn || actionBar && actionBar.drawn) {
+                return;
+            }
+            chartAPI.scrollcolumn2d._createToolBox.call(iapi);
+            toolBox = components.tb;
+            toolBoxGraphics = toolBox.graphics || (toolBox.graphics = {});
+            toolBoxAPI = components.toolBoxAPI || toolBox.getAPIInstances(toolBox.ALIGNMENT_HORIZONTAL);
+            Symbol = toolBoxAPI.Symbol;
+            SymbolStore = toolBoxAPI.SymbolStore;
+            group = (components.chartMenuBar || components.actionBar).componentGroups[0];
+
+            // Add the symbols in the chartMenuBar
+            zoomOutButton = toolBoxGraphics.zoomOutButton = new Symbol('zoomOutIcon', undefined, toolBox.idCount++,
+                toolBox.pId)
+            .attachEventHandlers({
+                'click' : function () {
+                    iapi.zoomOut();
+                },
+                'tooltext': tooltip && config.btnZoomOutTooltext || BLANK
+            });
+            resetButton = toolBoxGraphics.resetButton = new Symbol('resetIcon', undefined, toolBox.idCount++,
+                toolBox.pId)
+            .attachEventHandlers({
+                'click' : function () {
+                    iapi.resetZoom();
+                },
+                'tooltext': tooltip && config.btnResetChartTooltext || BLANK
+            });
+            if (allowPinMode) {
+                pinButton = toolBoxGraphics.pinButton = new Symbol('pinModeIcon', undefined, toolBox.idCount++,
+                    toolBox.pId)
+                .attachEventHandlers({
+                    'click' : function () {
+                        var info = config.viewPortConfig;
+                        iapi.activatePin(!info.pinned);
+                    },
+                    'tooltext': tooltip && config.btnSwitchToPinModeTooltext || BLANK
+                });
+                // append the instance of the buttons to the group
+                group.addSymbol(pinButton, true);
+            }
+            group.addSymbol(resetButton, true);
+            group.addSymbol(zoomOutButton, true);
+        },
+        _scrollBar: chartAPI.scrollcolumn2d,
+        _manageScrollerPosition: chartAPI.scrollcolumn2d,
+
+        draw: function () {
+            var borderWidth,
+                roundEdges,
+                info,
+                tAtt,
+                scrollBar,
+                scrollNode,
+                canvasLeft,
+                canvasTop,
+                canvasHeight,
+                canvasWidth,
+                canvasConfig,
+                canvasBorderWidth,
+                iapi = this,
+                config = iapi.config,
+                graphics = iapi.graphics || (iapi.graphics = {}),
+                components = iapi.components,
+                jsonData = iapi.jsonData,
+                dataSet = jsonData.dataset,
+                category = jsonData.categories && jsonData.categories[0].category,
+                toolboxParentGroup;
+            chartAPI.msline.draw.call (iapi);
+
+            canvasLeft = config.canvasLeft;
+            canvasTop = config.canvasTop;
+            canvasHeight = config.canvasHeight;
+            canvasWidth = config.canvasWidth;
+            canvasConfig = components.canvas.config;
+            canvasBorderWidth = canvasConfig.canvasBorderWidth;
+            borderWidth = config.borderWidth;
+            roundEdges = config.useRoundEdges;
+            info = config.viewPortConfig;
+            if (!(toolboxParentGroup = graphics.toolboxParentGroup)) {
+                toolboxParentGroup = graphics.toolboxParentGroup = components.paper.group('toolbarParentGroup',
+                    graphics.parentGroup);
+            }
+            if (dataSet && category) {
+
+                // Draw the scrollbars and perform the initial configuration of
+                // the scrollbars.
+                // First crispen the scroller position to keep it in sync with the
+                // canvas. We temporarily increment plotborderwidth to account for
+                // the 1 pixel stroke width of scrollbar.
+                borderWidth++;
+                tAtt = R.crispBound(canvasLeft - borderWidth, canvasTop + canvasHeight + borderWidth,
+                    canvasWidth + borderWidth + borderWidth, config.scrollHeight, borderWidth);
+                borderWidth--; // revert the temporary increment
+                // During creation of the scroller element, we check for the
+                // "useRoundEdges" options and based on that, match the visuals and
+                // positioning of the scrollbar with the canvas. We shift it up
+                // slightly when useRoundEdges is on; so that it covers the lower
+                // round edges of the canvas.
+                scrollBar = components.scrollBar;
+                scrollNode = scrollBar && scrollBar.node;
+                // donot draw the scroller element if there is no dataSet in the JSON data.
+                scrollBar.draw(tAtt.x + (roundEdges && -1 || (borderWidth % 2)), tAtt.y - (roundEdges && 4 || 2), {
+                    isHorizontal: true,
+                    width: tAtt.width - (!roundEdges && 2 || 0),
+                    height: tAtt.height,
+                    showButtons: config.scrollShowButtons,
+                    scrollRatio: info.vdl / (info.clen - !!info.clen), // standard
+                    scrollPosition: [info.dsi / (info.clen - info.vdl - 1), false],
+                    r: roundEdges && 2 || 0,
+                    parentLayer : toolboxParentGroup.insertBefore(graphics.datalabelsGroup)
+                });
+                // attach the callback for raising event only for it is a new scroll node.
+                !scrollNode && (function () {
+                    var prevPos;
+                    R.eve.on('raphael.scroll.start.' + scrollBar.node.id, function (pos) {
+                        prevPos = pos;
+                        iapi.graphics.crossline && iapi.graphics.crossline.disable(true);
+
+                        global.raiseEvent('scrollstart', {
+                            scrollPosition: pos
+                        }, iapi.chartInstance);
+                    });
+
+                    R.eve.on('raphael.scroll.end.' + scrollBar.node.id, function (pos) {
+                        iapi.graphics.crossline && iapi.graphics.crossline.disable(false);
+                        global.raiseEvent('scrollend', {
+                            prevScrollPosition: prevPos,
+                            scrollPosition: pos
+                        }, iapi.chartInstance);
+                    });
+                }());
+            }
+        }
+    }, chartAPI.msline, {
+        showValues: 0,
+        zeroplanethickness: 1,
+        zeroplanealpha: 40,
+        showzeroplaneontop: 0,
+        enablemousetracking: true
+    });
+
+    chartAPI('zoomlinedy',{
+        standaloneInit: true,
+        defaultDatasetType: 'zoomline',
+        applicableDSList: {
+            'zoomline': true
+        },
+        creditLabel: creditLabel,
+        friendlyName : 'Zoomable and Panable Multi-series Dual-axis Line Chart',
+        _spaceManager: chartAPI.msdybasecartesian._spaceManager,
+        _setAxisLimits: chartAPI.msdybasecartesian._setAxisLimits,
+        _createAxes: chartAPI.msdybasecartesian._createAxes,
+        _feedAxesRawData : chartAPI.msdybasecartesian._feedAxesRawData
+    }, chartAPI.zoomline, {
+        isdual: true
+    });
+
+    FusionCharts.register('component', ['dataset', 'zoomline', {
+        _setConfigure: function () {
+            var dataSet = this,
+                conf = dataSet.config,
+                cdef = dataSet.chart.jsonData.chart,
+                JSONData = dataSet.JSONData;
+            conf.drawanchors = pluckNumber(cdef.drawanchors, cdef.showanchors, 1);
+            conf.anchorradius = pluckNumber(JSONData.anchorradius, cdef.anchorradius, conf.linethickness + 2);
+            dataSet.__base__._setConfigure.apply(dataSet, arguments);
+        },
+        _firePlotEvent: function (eventType, plotIndex, e, datasetIndex) {
+            var dataset = this,
+                chart = dataset.chart,
+                components = dataset.components,
+                dataStore = components.dataRT || components.data,
+                data = dataStore[plotIndex],
+                setElement = data.graphics.element,
+                tip = lib.toolTip,
+                originalEvent = e.originalEvent,
+                style = chart.components.paper.canvas.style,
+                noCrossline = !dataset.chart.config.useCrossline,
+                config,
+                eventArgs,
+                setLink;
+
+            if (setElement) {
+                config = data.config;
+                setLink = config.setLink;
+                eventArgs = config.eventArgs;
+                switch (eventType) {
+                    case 'mouseover' :
+                        noCrossline && dataset._decideTooltipType(plotIndex, datasetIndex, e);
+                        dataset._rolloverResponseSetter(chart, data, originalEvent);
+                        setLink && (style.cursor = POINTER);
+                        break;
+                    case 'mouseout' :
+                        tip.hide(chart.chartInstance.id);
+                        dataset._rolloutResponseSetter(chart, data, originalEvent);
+                        setLink && (style.cursor = DEFAULT_CURSOR);
+                        break;
+                    case 'click' :
+                        plotEventHandler.call(setElement, chart, originalEvent, DATAPLOTCLICK, eventArgs);
+                        break;
+                    case 'mousemove' :
+                        noCrossline && dataset._decideTooltipType(plotIndex, datasetIndex, e);
+                }
+            }
+        },
+        configure: function () {
+            var pgsw,
+                conf,
+                pin = {},
+                dataSet = this,
+                cdef = dataSet.chart.jsonData.chart;
+            //no animation is supported.
+            cdef.animation = 0;
+            cdef.showvalues = pluckNumber(cdef.showvalues, 0);
+            dataSet.__base__.configure.call(dataSet);
+            conf = dataSet.config;
+            // pin line graphics is same as main graphics except a few changes
+            pgsw = conf.linethickness + pluckNumber(cdef.pinlinethicknessdelta, 1);
+            pin['stroke-width'] = pgsw > 0 && pgsw || 0;
+            pin['stroke-dasharray'] = [3, 2];
+            pin.stroke = lib.hashify(conf.linecolor);
+            pin['stroke-opacity'] = conf.alpha / 100;
+            pin['stroke-linejoin'] = (conf['stroke-linejoin'] = 'round');
+            pin['stroke-linecap'] = (conf['stroke-linecap'] = 'round');
+
+            conf.pin = pin;
+            // animation is disabled in zoomline charts.
+            conf.animation = false;
+            conf.transposeanimduration = 0;
+        },
+        isWithinShape: function (pointObj, pX, x, y) {
+                if (!pointObj) {
+                    return;
+                }
+
+                var anchorProps = pointObj.config.anchorProps,
+                    config = pointObj.config,
+                    radius,
+                    borderThickness = anchorProps.borderThickness,
+                    dataset = this,
+                    dataStore = dataset.components.data,
+                    dragTolerance = pluckNumber(config.dragTolerance, 0),
+                    xPos,
+                    yPos,
+                    dx,
+                    dy,
+                    diff,
+                    hoverEffects;
+
+                xPos = pointObj._xPos;
+                yPos = pointObj._yPos;
+
+                if (yPos !== null) {
+                    hoverEffects = pointObj.config.hoverEffects;
+                    radius =  Math.max(anchorProps.radius, hoverEffects && hoverEffects.anchorRadius || 0, HTP) +
+                        borderThickness / 2;
+                    dx = (x - xPos);
+                    dy = (y - yPos);
+                    diff = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
+
+                    if (diff <= radius || diff <= dragTolerance) {
+                        return {
+                            pointIndex: pX,
+                            hovered: true,
+                            pointObj: dataStore[pX]
+                        };
+                    }
+                    else {
+                        return false;
+                    }
+                }
+            },
+        draw: function () {// retrieve required objects
+            var //newElem,
+                graphics,
+                UNDEF = null,
+                flStart = false,
+                flEnd = false,
+                k,
+                flag1 = false,
+                flag2 = false,
+                dataSet = this,
+                JSONData = dataSet.JSONData,
+                chart = dataSet.chart,
+                chartComponents = chart.components,
+                conf = dataSet.config,
+                datasetIndex = dataSet.index || dataSet.positionIndex,
+                chartConfig = chart.config,
+                chartAttr = chart.jsonData.chart,
+                dataSetComponents = dataSet.components,
+                dataStore = dataSetComponents.data,
+                dataSetLen = dataStore.length,
+                len = dataSetLen,
+                i,
+                paper = chartComponents.paper,
+                xAxis = chartComponents.xAxis[0],
+                yAxis = dataSet.yAxis,
+                xPos,
+                yPos,
+                layers = chart.graphics,
+                dataLabelsLayer = layers.datalabelsGroup,
+                parseUnsafeString = lib.parseUnsafeString,
+                getValidValue = lib.getValidValue,
+                toolText,
+                label,
+                setElement,
+                //hotElement,
+                setLink,
+                setValue,
+                eventArgs,
+                displayValue,
+                groupId,
+                animationDuration = conf.animation.duration,
+                dataObj,
+                setRolloutAttr,
+                setRolloverAttr,
+                lineThickness = conf.linethickness,
+                container = dataSet.graphics.container,
+                trackerContainer = dataSet.graphics.trackerContainer,
+                // Called when clicking on the data plot
+                // @param {Array} setDataArr
+                clickFunc = function (setDataArr) {
+                    var ele = this;
+                    plotEventHandler.call(ele, chart, setDataArr);
+                },
+                /*
+                    Called when hovering over anchor
+                    @param {object} dataObj
+                    @param {boolean} hoverEnabled
+                */
+                // rolloverResponseSetter = function (data) {
+                //     var ele = this,
+                //         dataObj = ele.data('dataObj'),
+                //         hoverEnabled = dataObj.config.hoverEffects;
+                //     // Calling hoverplotanchor if hovereffects are enabled
+                //     if (hoverEnabled && dataObj && dataObj.graphics && dataObj.graphics.element) {
+                //         dataSet._hoverPlotAnchor(dataObj, ROLLOVER, chartAttr);
+                //     }
+                //     plotEventHandler.call(ele, chart, data, ROLLOVER);
+                // },
+                /*
+                    Called when hovering out of anchor
+                    @param {object} dataObj
+                    @param {boolean} hoverEnabled
+                */
+                // rolloutResponseSetter = function (data) {
+                //     var ele = this,
+                //     dataObj = ele.data('dataObj'),
+                //     hoverEnabled = dataObj.config.hoverEffects;
+                //     // Calling hoverplotanchor if hovereffects are enabled
+                //     if (hoverEnabled && dataObj && dataObj.graphics && dataObj.graphics.element) {
+                //         dataSet._hoverPlotAnchor(dataObj, ROLLOUT, chartAttr);
+                //     }
+                //     plotEventHandler.call(ele, chart, data, ROLLOUT);
+                // },
+                viewPortConfig = chartConfig.viewPortConfig,
+                showTooltip = chartConfig.showtooltip,
+                //attr,
+                group = layers.datasetGroup,
+                hoverEffects,
+                shadow = conf.shadow,
+                anchorShadow,
+                dataLabelContainer = dataSet.graphics.dataLabelContainer,
+                anchorProps = {},
+                imgRef,
+                addAttr,
+                symbol,
+                config,
+                is3D = chart.is3D,
+                use3dlineshift = conf.use3dlineshift,
+                showValue,
+                /*
+                    Called when transpose animation is completed
+                    for hiding the dataset
+                */
+                /*animCallBack = function() {
+                    lineElement.attr({
+                        path: currentPath.getPathArr()
+                    });
+                    if (dataSet.visible === false && (dataSet._conatinerHidden === false ||
+                            dataSet._conatinerHidden=== undefined)) {
+                        container.lineGroup.hide();
+                        container.lineShadowGroup.hide();
+                        container.anchorShadowGroup.hide();
+                        container.anchorGroup.hide();
+                        trackerContainer.hide();
+                        dataLabelContainer && dataLabelContainer.hide();
+                        // isLineSet ? (dataSet._conatinerHidden  = fcJSON.lineset._conatinerHidden) :
+                        //     (dataSet._conatinerHidden = true);
+                        dataSet._conatinerHidden = true;
+                    }
+                },*/
+                // animCompleteFn = chart.getAnimationCompleteFn(),
+                /*
+                    Called when the initial animation compeletes
+                    for showing the dataset
+                */
+                initAnimCallBack = function () {
+                    container.lineShadowGroup.show();
+                    container.anchorShadowGroup.show();
+                    container.anchorGroup.show();
+                    trackerContainer.show();
+                    dataLabelContainer && dataLabelContainer.show();
+                    chart._animCallBack();
+                    // animCompleteFn();
+                },
+                // animFlag = true,
+                setTooltext,
+                connector,
+                dashStyle,
+                yBase = yAxis.getAxisBase(),
+                yBasePos = yAxis.yBasePos = yAxis.getAxisPosition(yBase),
+                xAxisZeroPos = xAxis.getAxisPosition(0),
+                xAxisFirstPos = xAxis.getAxisPosition(1),
+                pointDistance = xAxisFirstPos - xAxisZeroPos,
+                totalCanvasWidth,
+                xDepth = is3D ? 10 : 0,
+                yDepth = (is3D && use3dlineshift) ? 10 : 0,
+                clipCanvas = [
+                    mathMax(0, chartConfig.canvasLeft - xDepth),
+                    mathMax(0, chartConfig.canvasTop - yDepth),
+                    mathMax(1, chartConfig.canvasWidth + xDepth * 2),
+                    mathMax(1, chartConfig.canvasHeight + yDepth)
+                ],
+                clipCanvasInit = [
+                    mathMax(0, chartConfig.canvasLeft - xDepth),
+                    mathMax(0, chartConfig.canvasTop - yDepth),
+                    1,
+                    mathMax(1, chartConfig.canvasHeight + yDepth * 2)
+                ],
+                scroll = {},
+                isScroll = chart.hasScroll || false,
+                scrollPosition,
+                lineDashStyle = conf.lineDashStyle,
+                lineColorObj = {
+                    color: conf.linecolor,
+                    alpha: conf.alpha
+                },
+                lscthash = [toRaphaelColor(lineColorObj), lineDashStyle].join(':'),
+                setColor,
+                setDashStyle,
+                lineSegmentChange,
+                colorObj,
+                lineDrawing = 0,
+                initialAnimation = false,
+                connectorShadow,
+                lineElement = dataSet.graphics.lineElement,
+                visible = dataSet.visible,
+                x,
+                dip,
+                pool = dataSet.pool || (dataSet.pool = {
+                    element: []
+                }),
+                removePath = {},
+                currentPath = {},
+                connectorPath = {},
+                anchorRadius = conf.anchorradius,
+                catLabel,
+                dataArr = [],
+                cntr,
+                j,
+                dataIndex,
+                strtIndex,
+                skippedVals,
+                endIndex,
+                showTerminalValidData = chartConfig.showTerminalValidData,
+                info = chartConfig.viewPortConfig,
+                showPeakData = chartConfig.showPeakData,
+                maxPeakDataLimit = chartConfig.maxPeakDataLimit,
+                minPeakDataLimit = chartConfig.minPeakDataLimit,
+                useCrossline = pluckNumber(chartConfig.useCrossline, 0),
+                step = info.step, // stepping that can fit target pixels per point
+                // minimum distance between two visible anchors
+                anchorminrenderdistance = xAxis.getPixel(info.step) - xAxisZeroPos,
+                // make anchors visible only when it is less than amrd.
+                hideAnchors = anchorminrenderdistance < info.amrd,
+                addAnchors = function (dataObj, dataIndex) {
+                    var graphics = dataObj.graphics;
+
+                    config = dataObj.config;
+                    setValue = config.setValue;
+                    setLink  = config.setLink;
+                    x = config.x || dataIndex;
+                    setTooltext = getValidValue(parseUnsafeString(pluck(config.setLevelTooltext,
+                        JSONData.plottooltext, chartAttr.plottooltext)));
+                    config.toolTipValue = yAxis.dataLabels(setValue);
+                    showValue = config.showValue;
+                    anchorProps = config.anchorProps;
+                    symbol = anchorProps.symbol;
+                    anchorShadow = anchorProps.shadow;
+                    displayValue = config.displayValue;
+                    dip = config.dip || 0;
+                    // Creating the data object if not created
+                    if (!dataObj) {
+                        dataObj = dataStore[dataIndex] = {
+                            graphics : {}
+                        };
+                    }
+                    // Storing the color Object of this data
+                    colorObj = {
+                        color: config.color,
+                        alpha: config.alpha
+                    };
+                    dashStyle = config.dashStyle;
+                    xPos = config.xPos || (xAxis.getAxisPosition(x) - xDepth);
+                    // On hiding a dataset the y position of the hidden dataset is set to yBasePos
+                    if (!dataSet.visible) {
+                        yPos = yBasePos;
+                    }
+                    else {
+                        yPos = yAxis.getAxisPosition(setValue) + yDepth;
+                    }
+                    hoverEffects = config.hoverEffects;
+                    anchorProps.isAnchorHoverRadius = hoverEffects.anchorRadius;
+
+                    label = config.label;
+                    catLabel = xAxis.getLabel(dataIndex);
+                    toolText = config.toolText + (setTooltext ? BLANK :
+                        config.toolTipValue);
+                    config.finalTooltext = toolText;
+                    // Storing the event arguments
+                    eventArgs = {
+                        index: dataIndex,
+                        link: setLink,
+                        value: setValue,
+                        displayValue: displayValue,
+                        categoryLabel: catLabel,
+                        toolText: toolText,
+                        id: conf.userID,
+                        datasetIndex: datasetIndex,
+                        datasetName: JSONData.seriesname,
+                        visible: visible
+                    };
+
+                    // donot create anchor elements if thats a null data.
+                    if (config.setValue !== null && !hideAnchors) {
+                        // If imageurl is present
+                        if (anchorProps.imageUrl) {
+                            imgRef = new Image();
+                            addAttr = {
+                                isTooltip : showTooltip,
+                                setLink : setLink,
+                                hotLayer : trackerContainer,
+                                groupId : groupId,
+                                clickFunc : clickFunc
+                            };
+                            imgRef.onload = dataSet._onAnchorImageLoad(dataSet, dataIndex, eventArgs, xPos, yPos);
+                            imgRef.onerror = dataSet._onErrorSetter(xPos, yPos, dataIndex, dataSet);
+                            imgRef.src = anchorProps.imageUrl;
+                        }
+                        else {
+                            setElement = graphics.element;
+                            if (!setElement) {
+                                // If there is no existing graphics element then create it
+                                if(pool.element && pool.element.length) {
+                                    setElement = graphics.element = pool.element.shift();
+                                }
+                                else {
+                                    setElement = graphics.element = paper.polypath(container.anchorGroup);
+                                }
+                            }
+                            // Set all attributes that are not related to non position
+                            setElement
+                            .attr({
+                                polypath: [anchorProps.symbol[1] || 2, xPos, yPos,
+                                anchorProps.radius, anchorProps.startAngle, dip],
+                                fill: toRaphaelColor({
+                                    color: anchorProps.bgColor,
+                                    alpha: anchorProps.bgAlpha
+                                }),
+                                stroke: toRaphaelColor({
+                                    color: anchorProps.borderColor,
+                                    alpha: anchorProps.borderAlpha
+                                }),
+                                'stroke-width': anchorProps.borderThickness,
+                                'visibility': !anchorProps.radius ? 'hidden' : visible
+                            })
+                            .shadow(anchorShadow, container.anchorShadowGroup)
+                            .data('anchorRadius', anchorProps.radius)
+                            .data('anchorHoverRadius', hoverEffects.anchorRadius)
+                            .data('hoverEnabled', hoverEffects.enabled)
+                            .data('eventArgs',eventArgs);
+
+                            if (hoverEffects.enabled) {
+                                setRolloverAttr = {
+                                    polypath: [hoverEffects.anchorSides || 2,
+                                                xPos, yPos,
+                                                hoverEffects.anchorRadius,
+                                                hoverEffects.startAngle,
+                                                hoverEffects.dip
+                                            ],
+                                    fill: toRaphaelColor({
+                                        color: hoverEffects.anchorColor,
+                                        alpha: hoverEffects.anchorBgAlpha
+                                    }),
+                                    stroke: toRaphaelColor({
+                                        color: hoverEffects.anchorBorderColor,
+                                        alpha: hoverEffects.anchorBorderAlpha
+                                    }),
+                                    'stroke-width': hoverEffects.anchorBorderThickness
+                                };
+                                setRolloutAttr = {
+                                    polypath: [anchorProps.sides, xPos, yPos,
+                                                anchorProps.radius, anchorProps.startAngle, dip
+                                            ],
+                                    fill: toRaphaelColor({
+                                        color: anchorProps.bgColor,
+                                        alpha: anchorProps.bgAlpha
+                                    }),
+                                    stroke: toRaphaelColor({
+                                        color: anchorProps.borderColor,
+                                        alpha: anchorProps.borderAlpha
+                                    }),
+                                    'stroke-width': anchorProps.borderThickness
+                                };
+
+                                setElement && setElement
+                                .data('setRolloverAttr', setRolloverAttr)
+                                .data('setRolloutAttr', setRolloutAttr);
+                            }
+                            // hide the anchor element if the data is that of a null data
+                            setElement[(setValue || (setValue === 0)) ? 'show' : 'hide']();
+                        }
+
+                        // anchor Radius of hot element is set to maximum of hover radius and anchor radius
+                        anchorRadius = mathMax(anchorProps.radius,
+                            hoverEffects &&
+                            hoverEffects.anchorRadius || 0);
+                    }
+                    dataObj._xPos = xPos;
+                    dataObj._yPos = yPos;
+                    /*
+                        if colorObj and dash style of this data is different from
+                        the previous data then a new line segment is to be created
+                    */
+                    lineSegmentChange = (lscthash !== [
+                        toRaphaelColor(colorObj || lineColorObj),
+                        dashStyle || lineDashStyle
+                    ].join(':'));
+                    connector = dataObj.graphics.connector;
+                    connectorPath = dataSet.getLinePath([dataObj], connectorPath);
+                    setColor = toRaphaelColor(colorObj || lineColorObj);
+                    if (colorObj) {
+                        connectorShadow = {
+                            opacity: colorObj && colorObj.alpha/100
+                        };
+                    }
+                    else {
+                        connectorShadow = shadow;
+                    }
+                    setDashStyle = dashStyle || lineDashStyle;
+                    /*If color,alpha or dashed is not defined for this data
+                    then line drawing is set to 1 so the path of this data is
+                    appended to mainLinePath and not the connector path
+                    */
+                    if (pluck(config.setColor, config.setAlpha, config.setDashed) === UNDEFINED) {
+                        lineDrawing = 1;
+                    }
+                    else {
+                        lineDrawing = 0;
+                    }
+                    lscthash = [setColor, setDashStyle].join(':');
+                    /*Storing the x position and y position in dataObject for future reference
+                    For example - when drawing labels we need this xPos and yPos
+                    */
+                    showValue && !anchorProps.imageUrl && dataSet.drawLabel(dataIndex);
+                    dataArr.push(dataObj);
+                },
+                renderPeakData = function(cVal, index) {
+                    var numVals = cVal && cVal.length,
+                        orderArr = cVal.slice().sort(function (a, b){
+                            return a.config.setValue - b.config.setValue;
+                        }),
+                        maxVal = orderArr && orderArr.pop().config.setValue,
+                        minVal = (orderArr.length && orderArr.shift().config.setValue) || maxVal,
+                        counter = 0,
+                        origIndex,
+                        val;
+                    //now if any value is above or below of the
+                    //peak limits enter into iteration. This is to maximize
+                    //performance.
+                    if(maxVal > maxPeakDataLimit || minVal < minPeakDataLimit) {
+                        while(counter < numVals) {
+                            setElement = cVal[counter];
+                            val = setElement.config.setValue;
+                            if(val > maxPeakDataLimit || val < minPeakDataLimit) {
+                                origIndex = index + counter;
+                                //we have a sudden peak. need to show this
+                                addAnchors(setElement, origIndex);
+                            }
+                            counter += 1;
+                        }
+                    }
+
+                },
+                reuseFN = function (start, end) {
+                    // todo: Need to re-validate the use of this. Its a safer approach to remove one elements each
+                    // from both sides
+                    start -= 1;
+                    end += 1;
+                    // todo ends.
+                    var prop;
+                    for (i = start; i < end; i += 1) {
+                        graphics = dataStore[i] && dataStore[i].graphics || {};
+                        dataStore[i] && (dataStore[i].config.isRemoving = true);
+                        for (prop in graphics) {
+                            if (!pool[prop]) {
+                                pool[prop] = [];
+                            }
+                            if (graphics[prop]) {
+                                pool[prop].push(graphics[prop].hide());
+                                graphics[prop] = undefined;
+                            }
+                        }
+                    }
+                },
+                ddsi = (viewPortConfig.ddsi || 0),
+                ii = viewPortConfig.ddei || len,
+                _oldStartIndex = conf._oldStartIndex,
+                _oldEndIndex = conf._oldEndIndex,
+                _oldStep = conf._oldStep,
+                removeDataArr = dataSetComponents.removeDataArr,
+                removeDataArrLen = removeDataArr && removeDataArr.length;
+
+            // Creating the line group and appending it to dataset layer if not created
+            group.line = group.line ||
+                paper.group('line', group);
+            /*
+             * Creating lineConnector group and appending it to dataset layer if not created
+             * Lineconnector group has the anchorgroups of all datasets
+             */
+            group.lineConnector = group.lineConnector ||
+                paper.group('line-connector', group);
+            // Create dataset container if not created
+            if (!container) {
+                container = dataSet.graphics.container = {
+                    lineShadowGroup: paper.group('connector-shadow', group.line),
+                    anchorShadowGroup: paper.group('anchor-shadow', group.lineConnector),
+                    lineGroup: paper.group('line', group.line),
+                    anchorGroup: paper.group('anchors', group.lineConnector)
+                };
+                container.lineGroup.trackTooltip(true);
+                if (!visible) {
+                    container.lineShadowGroup.hide();
+                    container.anchorShadowGroup.hide();
+                    container.lineGroup.hide();
+                    container.anchorGroup.hide();
+                }
+            }
+            // Create tracker container if not created
+            if (!trackerContainer) {
+                trackerContainer = dataSet.graphics.trackerContainer = paper.group('line-hot',layers.trackerGroup)
+                .toBack();
+                if (!visible) {
+                    trackerContainer.hide();
+                }
+            }
+
+            if (!dataStore) {
+                dataStore = dataSet.components.data = [];
+            }
+
+            if (!dataLabelContainer) {
+                dataLabelContainer = dataSet.graphics.dataLabelContainer = dataSet.graphics.dataLabelContainer ||
+                    paper.group('datalabel', dataLabelsLayer);
+                if (!visible) {
+                    dataLabelContainer.hide();
+                }
+            }
+            /*  Calculating total canvas width
+                Required for scroll charts
+            */
+            totalCanvasWidth = pointDistance * len;
+            // if anchors were shown in previous state and now hidden due to amrd constrain, hide them all of the prev.
+            if (hideAnchors && !conf._oldHideAnchors) {
+                reuseFN(_oldStartIndex, _oldEndIndex);
+            }
+            else {
+                // If step values are changed while zoom in/out, remove all of the prev anchors.
+                if (step !== _oldStep) {
+                    reuseFN(_oldStartIndex, _oldEndIndex);
+                }
+                // useful in case of scroll. Here the step values are not changed. Hence removing optimally.
+                else {
+                    if (ddsi > _oldStartIndex) {
+                        reuseFN(_oldStartIndex, (ddsi > _oldEndIndex) ? _oldEndIndex : ddsi);
+                    }
+                    if (ii < _oldEndIndex) {
+                        reuseFN((ii < _oldStartIndex) ? _oldStartIndex : ii, _oldEndIndex);
+                    }
+                    if (ddsi < _oldStartIndex || ii > _oldEndIndex) {
+                        reuseFN(_oldStartIndex, _oldEndIndex);
+                    }
+                }
+            }
+            // caching the present state for future reference.
+            conf._oldHideAnchors = hideAnchors;
+            conf._oldEndIndex = ii;
+            conf._oldStep = step;
+            dataSet.setVisibility(visible);
+            //create plot elements
+            for (i = (conf._oldStartIndex = ddsi); i <= ii; i += (step)) {
+                dataObj = dataStore[i] || {};
+                config = dataObj.config || {};
+                config.isRemoving = false;
+                setValue = config.setValue || UNDEF;
+                dataIndex = i;
+                if (showTerminalValidData) {
+                    // search for first valid data point and first step valid data point
+                    // true if first data value is undefined
+                    if ((i === 0) && (setValue === UNDEF)) {
+                        cntr = 0;
+                        for (j = k = i; j < len;) {
+
+                            // search for first valid data point and set flag1 true,
+                            // if first valid data point found
+                            if ((dataStore[j].config.setValue === UNDEF) && !flag1) {
+                                j++;
+                            }
+                            else {
+                                flag1 = true;
+                            }
+
+                            // search for first valid step data point and set flag2 true,
+                            // if first valid data point found
+                            if ((dataStore[k].config.setValue === UNDEF) && (!flag2) && (k <= len)) {
+                                k += step;
+                                cntr++;
+                            }
+                            else {
+                                flag2 = true;
+                            }
+
+                            // check both flag value
+                            // terminates loop if both first valid data point and first valid step data point found
+                            if (flag1 && flag2) {
+                                flag1 = flag2 = false;
+                                break;
+                            }
+                        }
+                        if ( j % step !== 0) {
+                            flStart = true;
+                            config = dataStore[j].config;
+                            dataIndex = j;
+                        }
+                    }
+
+                    // search for last valid data point and last step valid data point
+                    // true if i exceeds data length
+                    else if ((i >= len) && (setValue === UNDEF)) {
+                        for (j = k = i; j > 0;) {
+
+                            // search for last valid data point and set flag1 true,
+                            // if last valid data point found
+                            if ((dataStore[j] === undefined) && !flag1) {
+                                j--;
+                            }
+                            else {
+                                flag1 = true;
+                            }
+
+                            // search for last valid step data point and set flag2 true,
+                            // if last valid data point found
+                            if (dataStore[k] === undefined && (!flag2)  && (k >= 0)) {
+                                k -= step;
+                            }
+                            else {
+                                flag2 = true;
+                            }
+
+                            // check both flag value
+                            // terminates loop if both last valid data point and last valid step data point found
+                            if (flag1 && flag2) {
+                                break;
+                            }
+                        }
+                        if ( j % step !== 0) {
+                            flEnd = true;
+                            config = dataStore[j].config;
+                            dataIndex = j;
+                        }
+                    }
+                }
+                dataObj = dataStore[dataIndex];
+                if (!dataObj) {
+                    continue;
+                }
+                addAnchors(dataObj, dataIndex);
+                // No point searching for peak when ther is no stepping
+                if(showPeakData && step > 1) {
+                    strtIndex = mathMin((i + 1), ii);
+                    endIndex = mathMin((strtIndex + step), ii);
+                    skippedVals = (endIndex === ii) ? dataStore.slice(strtIndex) : dataStore.slice(strtIndex,
+                        endIndex);
+                    skippedVals.length && renderPeakData(skippedVals, strtIndex);
+                }
+            }
+            currentPath = dataSet.getLinePath(dataArr, {});
+            removePath = dataSet.getLinePath(dataArr, removePath);
+            conf.lastPath = currentPath;
+            if (!lineElement) {
+                if (pool.lineElement && pool.lineElement.length) {
+                    lineElement = dataSet.graphics.lineElement = pool.lineElement.shift();
+                }
+                else {
+                    lineElement = dataSet.graphics.lineElement = paper.path(container.lineGroup);
+                }
+            }
+            // Show tooltip if crossline is disabled
+            if (!useCrossline) {
+                if (!lineElement.tooltipListenerAttached) {
+                    lineElement.tooltipListenerAttached = true;
+                    lineElement.mousemove(function (e) {
+                        info = chartConfig.viewPortConfig;
+                        var plotX = chartConfig._visx,
+                            step = info.step,
+                            stepw = info.ppp * step,
+                            x = getMouseCoordinate(chart.linkedItems.container, e, chart).chartX - plotX,
+                            pos,
+                            parsedLabel,
+                            tooltipText,
+                            tooltipTextSeparator = chartConfig.tooltipSepChar;
+                        x = (x += (stepw / 2) + info.offset) - x % (stepw);
+                        pos = (pos = (chart.getValuePixel(x))) + pos % step;
+                        parsedLabel = xAxis.getLabel(pos).label;
+
+                        // Initial tooltip text
+                        tooltipText = parsedLabel + tooltipTextSeparator +
+                            dataSet.components.data[pos].config.formatedVal;
+                        // Add plot name if exists in tooltip text
+                        tooltipText = conf.seriesname && (conf.seriesname + tooltipTextSeparator + tooltipText) ||
+                            tooltipText;
+                        lineElement.tooltip(chartConfig.crossline.enabled === 0 ? tooltipText : false);
+                    });
+                }
+            }
+            lineElement
+            .attr({
+                path: currentPath.getPathArr(),
+                'stroke-dasharray': lineDashStyle,
+                'stroke-width': lineThickness,
+                stroke: toRaphaelColor(lineColorObj),
+                'stroke-linecap': 'round',
+                /*  for lines even with thickness as 2 we need to have round line join
+                otherwise the line join may look like exceeding the correct position
+                */
+                'stroke-linejoin': lineThickness >= MAX_MITER_LINEJOIN ? 'round' : 'miter'
+            })
+            .shadow(shadow, container.lineShadowGroup);
+
+            // Animation from right when scrollToEnd is 1
+            if (isScroll) {
+                scrollPosition = scroll.startPercent;
+                clipCanvas[2] = totalCanvasWidth + clipCanvasInit[0];
+                if (scrollPosition === 1) {
+                    clipCanvasInit[0] = clipCanvas[2];
+                    clipCanvas[0] = 0;
+                }
+            }
+            clipCanvas[3] = clipCanvas[3] + yDepth;
+            if (animationDuration && visible && initialAnimation) {
+                container.anchorGroup.hide();
+                container.lineShadowGroup.hide();
+                container.anchorShadowGroup.hide();
+                dataLabelContainer.hide();
+                group.line.attr({
+                    'clip-rect': clipCanvasInit
+                })
+                .animate({
+                    'clip-rect': clipCanvas
+                }, animationDuration, 'normal', initAnimCallBack);
+            }
+            dataSet.drawn = true;
+            removeDataArrLen && dataSet.remove();
+        },
+        setVisibility: function (isVisible, isTranspose) {
+            var dataSet = this,
+                graphics = dataSet.graphics,
+                container = graphics && graphics.container,
+                trackerContainer = graphics && graphics.trackerContainer,
+                dataLabelContainer = graphics && graphics.dataLabelContainer,
+                action = isVisible ? 'show' : 'hide';
+            container.lineGroup[action]();
+            container.anchorGroup[action]();
+            container.anchorShadowGroup[action]();
+            container.lineShadowGroup[action]();
+            trackerContainer[action]();
+            dataLabelContainer[action]();
+
+            isTranspose && dataSet.transposeLimits(isVisible);
+
+        },
+        transposeLimits: function (isVisible) {
+            var dataSet = this,
+                chart = dataSet.chart,
+                chartConfig = chart.config,
+                yAxis = dataSet.yAxis;
+
+            chart._chartAnimation();
+            dataSet.visible = isVisible;
+            dataSet._conatinerHidden = !isVisible;
+
+            chart._setAxisLimits();
+            yAxis.draw();
+            chartConfig.legendClicked = true;
+            // Calling the draw function for redrawing the dataset
+            chart._drawDataset();
+            delete chartConfig.legendClicked;
+        },
+        /*
+         * Hides when clicked on its respective legend
+         * Fired every time an activated legend is clicked
+         */
+        hide : function() {
+            this.setVisibility(false, true);
+        },
+        show : function() {
+            this.setVisibility(true, true);
+        }
+    },'Line']);
+
+    CrossLine = function ()  {};
+
+    CrossLine.prototype.configure = function (chart, options) {
+        // Create the tracker for cross-hair. This is needed for mouse
+        // tracking.
+        var i,
+            ii,
+            plot,
+            plotColor,
+            attrObj = {},
+            crossLine = this,
+            labelPadding = 2.5,
+            chartComponents = chart.components,
+            numberFormatter = chartComponents.numberFormatter,
+            paper = chartComponents.paper,
+            chartConfig = chart.config,
+            chartGraphics = chart.graphics,
+            plotX = this.left = chartConfig._visx,
+            plotY = this.top = chartConfig.canvasTop,
+            plotH = this.height = chartConfig.canvasHeight,
+            plotO = this._visout = chartConfig._visout,
+            plots = this.plots = chart.components.dataset,
+            datalayer = chartGraphics.datalabelsGroup,
+            group,
+            line,
+            labelStyle = options.labelstyle,
+            valueStyle = options.valuestyle,
+            pyaxis = chartComponents.yAxis[0],
+            pYAxisLimits = pyaxis.getLimit(),
+            syaxis = chartComponents.yAxis[1],
+            sYAxisLimits = syaxis && syaxis.getLimit(),
+            tracker = this.tracker,
+            labels = this.labels,
+            positionLabel = this.positionLabel,
+            linkedItems = chart.get('linkedItems'),
+            containerElem = linkedItems.container,
+            listeners = linkedItems.eventListeners || (linkedItems.eventListeners = []),
+            isWithinCanvas = function (e, fn, fnOut) {
+                // calls the function only if it is within the canvasArea.
+                if (chart.isWithinCanvas(e, chart).insideCanvas) {
+                    fn.call(crossLine, e);
+                }
+                else {
+                    fnOut.call(crossLine, e);
+                }
+            };
+
+        crossLine.width = chartConfig._visw;
+        // Create the group inside data layer where the cross-line elements will play around.
+        group = this.group;
+        if (!group) {
+            group = this.group = paper.group ('crossline-labels', datalayer);
+            // Store chart's container to be use by mouseMove event
+            // to calculate the mouse coordinates.
+            this.container = containerElem;
+        }
+        group
+        .attr ( {
+            transform: ['T', plotX, chartConfig._ymin]
+        })
+        .css(valueStyle);
+
+        // Cross-line needs a personal tracker to intercept mouse interactions around and over data plots.
+        if (!tracker) {
+            tracker = crossLine.tracker = containerElem;
+
+            //adds to event stack.
+            listeners.push(addEvent(containerElem, 'touchstart mousemove', function (e) {
+                isWithinCanvas(e, crossLine.onMouseMove, crossLine.onMouseOut);
+            }, crossLine));
+
+            //adds to event stack.
+            listeners.push(addEvent(containerElem, 'mousedown', function () {
+                crossLine.onMouseDown();
+            }, crossLine));
+
+            //adds to event stack.
+            listeners.push(addEvent(containerElem, 'mouseup', function () {
+                crossLine.onMouseUp();
+            }, crossLine));
+
+            //adds to event stack.
+            listeners.push(addEvent(containerElem, 'mouseout', function () {
+                crossLine.onMouseOut();
+            }, crossLine));
+        }
+        /*tracker
+        .attr ({
+            x: plotX,
+            y: plotY,
+            width: plotW,
+            height: plotH,
+            stroke: 'none',
+            'stroke-width': 0,
+            fill: TRACKER_FILL
+        });*/
+
+        // Cross-line obviously needs a line.
+        line = this.line;
+
+        if (!line) {
+            line = this.line = paper.path(datalayer)
+            .toBack ();
+        }
+        line
+        .attr (extend2 ( {
+            path: ['M', plotX, plotY, 'l', 0, plotH]
+        }, options.line));
+
+        if (!labels) {
+            labels = this.labels = options.valueEnabled && paper.set ();
+        }
+        // add the category label
+        if (options.labelEnabled) {
+            attrObj.x = plotO;
+            attrObj.y = plotY + plotH + (chartConfig.scrollHeight || 0) + labelPadding;
+            attrObj['vertical-align'] = 'top';
+            attrObj.direction = chartConfig.textDirection;
+            attrObj.text = BLANK;
+            if (!positionLabel) {
+                positionLabel = this.positionLabel = paper.text(attrObj, labelStyle, chartGraphics.datalabelsGroup)
+                .insertBefore (chartGraphics.datasetGroup);
+            }
+            else {
+                positionLabel.attr(attrObj);
+                positionLabel.css(labelStyle);
+            }
+        }
+        else {
+            positionLabel && positionLabel.remove();
+            delete this.positionLabel;
+        }
+
+        // initially hidden
+        this.hide ();
+        this.ppixelRatio = -pyaxis.getPVR();
+        this.spixelRatio = syaxis && -syaxis.getPVR();
+        this.yminValue = chartConfig._yminValue;
+        this.pyaxisminvalue = pYAxisLimits.min;
+        this.pyaxismaxvalue = pYAxisLimits.max;
+        this.syaxisminvalue = sYAxisLimits && sYAxisLimits.min;
+        this.syaxismaxvalue = sYAxisLimits && sYAxisLimits.max;
+        this.positionLabels = chartConfig.xlabels || {
+            data: [],
+            parsed: []
+        };
+        this.chart = chart;
+
+        // Open closed function to access chart variables.
+        this.getZoomInfo = function () {
+            return chartConfig.viewPortConfig;
+        };
+
+        this.getDataIndexFromPixel = function (px) {
+            return Math.round(chart.components.xAxis[0].getValue(px));
+        };
+
+        this.getPositionLabel = function (index) {
+            var text = chart.components.xAxis[0].getLabel(index);
+            return (text && text.label) || BLANK;
+        };
+
+        if (options.valueEnabled) {
+            for (i = 0, ii = plots.length; i < ii; i += 1) {
+                plot = plots[i];
+                plotColor = hashify(plot.config.linecolor);
+
+                attrObj.x = 0;
+                attrObj.y = plotO;
+                attrObj.fill = plotColor;
+                attrObj.direction = chartConfig.textDirection;
+                attrObj.text = BLANK;
+                attrObj['text-bound'] = valueStyle['text-bound'];
+                attrObj.lineHeight = valueStyle.lineHeight;
+                if (!labels[i]) {
+                    labels[i] = labels.items[i] = paper.text(attrObj, UNDEFINED, group);
+                }
+                else {
+                    labels[i]
+                    .attr(attrObj);
+                    // .css (valueStyle);
+                }
+            }
+            for (; i < labels.items.length; i += 1) {
+                labels[i].remove();
+                delete labels[i];
+                labels.items.splice(i,1);
+            }
+            this.numberFormatter = numberFormatter;
+        }
+        else if (labels.items && labels.items.length) {
+            for (i = 0; i < labels.items.length; i += 1) {
+                labels[i].remove();
+                delete labels[i];
+            }
+            labels.length = 0;
+        }
+    };
+    CrossLine.prototype.disable = function (state) {
+        if (state !== UNDEF) {
+            this.disabled = !!state;
+            if (this.disabled && this.visible) {
+                this.hide ();
+            }
+        }
+        return this.disabled;
+    };
+
+    CrossLine.prototype.onMouseOut = function () {
+        this.hide ();
+        this.position = undefined;
+    };
+
+    CrossLine.prototype.onMouseDown = function () {
+        !hasTouch && this.hide ();
+        this._mouseIsDown = true;
+    };
+
+    CrossLine.prototype.onMouseUp = function () {
+        !hasTouch && this.hide ();
+        delete this._mouseIsDown;
+    };
+
+    CrossLine.prototype.onMouseMove = function (e) {
+        if (this.disabled || (this._mouseIsDown && !hasTouch)) {
+            return;
+        }
+        var residue,
+            info = this.getZoomInfo (),
+            line = this.line,
+            plotX = this.left,
+            step = info.step,
+            chart = this.chart,
+            xAxis = chart.components.xAxis[0],
+            chartConfig = chart.get('config'),
+            canvasLeft = chartConfig.canvasLeft,
+            axisDimention = xAxis.getAxisConfig('axisDimention'),
+            // labelStep = xAxis.getAxisConfig('labelStep'),
+            x = getMouseCoordinate (this.container, e, chart).chartX - plotX,
+            xAxisVisible = xAxis._getVisibleConfig(),
+            // cdm = chart.config.cdm,
+            //bridge code.
+            /*bridgePixel = cdm ? (xAxis.getPixel(0) - xAxis.getPixel(axisMin)) / info.scaleX :
+                axisDimention.x - canvasLeft,*/
+            bridgePixel = axisDimention.x - canvasLeft,
+            pos;
+        // x = (x += (stepw / 2) + info.offset) - x % (stepw);
+        pos = (pos = this.getDataIndexFromPixel (mathRound (x))) + ((((residue =
+            (pos % step)) > (step / 2)) ? step - residue : -residue));
+        x = (xAxis.getPixel(pos) - bridgePixel) - canvasLeft;
+        // x -= info.offset;
+        line.transform(['T', mathRound(x), 0]);
+        (this.hidden && (chartConfig.crossline.enabled !== 0)) && this.show ();
+        // Donot show the crossline out of canvas area.
+        (pos < xAxisVisible.minValue || pos > xAxisVisible.maxValue) && (this.hide());
+        if (pos !== this.position || this.hidden) {
+            this.position = pos;
+            this.lineX = x;
+            this.updateLabels ();
+        }
+    };
+
+    CrossLine.prototype.updateLabels = function () {
+        var crossline = this,
+            labelPadding = 2.5,
+            labels = crossline.labels,
+            plots = crossline.plots,
+            visw = crossline.width,
+            position = crossline.position,
+            x = crossline.lineX,
+            flooredX = mathFloor (x),
+            ppvr = crossline.ppixelRatio,
+            spvr = crossline.spixelRatio,
+            yminValue = crossline.yminValue,
+            plotOut = crossline._visout,
+            numberFormatter = crossline.numberFormatter,
+            pYAxisMinValue = crossline.pyaxisminvalue,
+            pYAxisMaxValue = crossline.pyaxismaxvalue,
+            sYAxisMinValue = crossline.syaxisminvalue,
+            sYAxisMaxValue = crossline.syaxismaxvalue,
+            verticalLimits = (function () {
+                /*
+                 * This controls the overlapping of the crosshair labels if the plots are close.
+                 * This calculates the space to display the labels at the before it is plotted in DOM.
+                 * The buleprint of the algorithm is like:
+                 * When the labels position is being calculated.
+                 * a) Place the label if the position is not occupied
+                 * b) If the position is occupied find the neares unoccupied space
+                 * c) If the unoccupied space is downwards place it there
+                 * d) If the unoccupied space is upwards move the whole system up and place it in the next available
+                 * down position
+                 */
+                var _top = crossline.height * (-1),
+                    _bottom = yminValue * ppvr,
+                    boxHeight = 0,  // Height of text
+                    margin = 2,
+                    oMatrix,
+                    result = { },
+                    isSpaceEnough = false,
+                    sections,
+                    abs = Math.abs,
+                    floor = Math.floor,
+                    scale = { };
+
+                /*
+                * Polyfills
+                * Ofcourse and obviously for IE8
+                */
+                // Pollyfills for Object.create
+                // Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
+                if (typeof Object.create != 'function') {
+                    // Production steps of ECMA-262, Edition 5, 15.2.3.5
+                    // Reference: http://es5.github.io/#x15.2.3.5
+                    /*jslint freeze: false */
+                    Object.create = (function () {
+                        // To save on memory, use a shared constructor
+                        function Temp () { }
+
+                        // make a safe reference to Object.prototype.hasOwnProperty
+                        var hasOwn = Object.prototype.hasOwnProperty;
+
+                        return function (O) {
+                            var Properties, prop, obj;
+                            // 1. If Type (O) is not Object or Null throw a TypeError exception.
+                            if (typeof O != 'object') {
+                                throw new TypeError ('Object prototype may only be an Object or null');
+                            }
+
+                            // 2. Let obj be the result of creating a new object as if by the
+                            //    expression new Object () where Object is the standard built-in
+                            //    constructor with that name
+                            // 3. Set the [[Prototype]] internal property of obj to O.
+                            Temp.prototype = O;
+                            obj = new Temp ();
+                            Temp.prototype = null; // Let's not keep a stray reference to O...
+
+                            // 4. If the argument Properties is present and not undefined, add
+                            //    own properties to obj as if by calling the standard built-in
+                            //    function Object.defineProperties with arguments obj and
+                            //    Properties.
+                            if (arguments.length > 1) {
+                                // Object.defineProperties does ToObject on its first argument.
+                                Properties = Object (arguments[1]);
+                                for (prop in Properties) {
+                                    if (hasOwn.call (Properties, prop)) {
+                                        obj[prop] = Properties[prop];
+                                    }
+                                }
+                            }
+
+                            // 5. Return obj
+                            return obj;
+                        };
+                    }) ();
+                }
+
+                // Pollyfills for Array indexOf
+                // Ref: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
+                if (!Array.prototype.indexOf) {
+                    /*jslint freeze: false */
+                    Array.prototype.indexOf = function (searchElement, fromIndex) {
+
+                        var k, O, len, n;
+
+                        // 1. Let O be the result of calling ToObject passing
+                        //    the this value as the argument.
+                        if (this == null) {
+                            throw new TypeError ('"this" is null or not defined');
+                        }
+
+                        O = Object (this);
+
+                        // 2. Let lenValue be the result of calling the Get
+                        //    internal method of O with the argument "length".
+                        // 3. Let len be ToUint32 (lenValue).
+                        len = O.length >>> 0;
+
+                        // 4. If len is 0, return -1.
+                        if (len === 0) {
+                            return -1;
+                        }
+
+                        // 5. If argument fromIndex was passed let n be
+                        //    ToInteger (fromIndex); else let n be 0.
+                        n = +fromIndex || 0;
+
+                        if (Math.abs (n) === Infinity) {
+                            n = 0;
+                        }
+
+                        // 6. If n >= len, return -1.
+                        if (n >= len) {
+                            return -1;
+                        }
+
+                        // 7. If n >= 0, then Let k be n.
+                        // 8. Else, n<0, Let k be len - abs (n).
+                        //    If k is less than 0, then let k be 0.
+                        k = Math.max (n >= 0 ? n : len - Math.abs (n), 0);
+
+                        // 9. Repeat, while k < len
+                        while (k < len) {
+                            // a. Let Pk be ToString (k).
+                            //   This is implicit for LHS operands of the in operator
+                            // b. Let kPresent be the result of calling the
+                            //    HasProperty internal method of O with argument Pk.
+                            //   This step can be combined with c
+                            // c. If kPresent is true, then
+                            //    i.  Let elementK be the result of calling the Get
+                            //        internal method of O with the argument ToString (k).
+                            //   ii.  Let same be the result of applying the
+                            //        Strict Equality Comparison Algorithm to
+                            //        searchElement and elementK.
+                            //  iii.  If same is true, return k.
+                            if (k in O && O[k] === searchElement) {
+                                return k;
+                            }
+                            k++;
+                        }
+                        return -1;
+                    };
+                }
+
+                // Pollyfills for Array forEach
+                // Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
+                if (!Array.prototype.forEach) {
+                    // Production steps of ECMA-262, Edition 5, 15.4.4.18
+                    // Reference: http://es5.github.io/#x15.4.4.18
+                    /*jslint freeze: false */
+                    Array.prototype.forEach = function (callback, thisArg) {
+
+                        var T, k, O, len, kValue;
+
+                        if (this == null) {
+                            throw new TypeError (' this is null or not defined');
+                        }
+
+                        // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
+                        O = Object (this);
+
+                        // 2. Let lenValue be the result of calling the Get internal method of O
+                        // with the argument "length".
+                        // 3. Let len be ToUint32 (lenValue).
+                        len = O.length >>> 0;
+
+                        // 4. If IsCallable (callback) is false, throw a TypeError exception.
+                        // See: http://es5.github.com/#x9.11
+                        if (typeof callback !== 'function') {
+                            throw new TypeError (callback + ' is not a function');
+                        }
+
+                        // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
+                        if (arguments.length > 1) {
+                            T = thisArg;
+                        }
+
+                        // 6. Let k be 0
+                        k = 0;
+
+                        // 7. Repeat, while k < len
+                        while (k < len) {
+                            // a. Let Pk be ToString (k).
+                            //   This is implicit for LHS operands of the in operator
+                            // b. Let kPresent be the result of calling the HasProperty internal method of O
+                            // with argument Pk.
+                            //   This step can be combined with c
+                            // c. If kPresent is true, then
+                            if (k in O) {
+
+                                // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
+                                kValue = O[k];
+
+                                // ii. Call the Call internal method of callback with T as the this value and
+                                // argument list containing kValue, k, and O.
+                                callback.call (T, kValue, k, O);
+                            }
+                            // d. Increase k by 1.
+                            k++;
+                        }
+                        // 8. return undefined
+                    };
+                }
+
+                // Attachable label can be positioned / attached anywhere along the y axis.
+                // If its position is changed via shifting or providing index it auto calculates the y.
+                function AttachableLabel () {
+                    this.y = 0;  // Absolute position
+                    this.lRef = undefined;   // Label object itself
+                    this.__shift = 0;    // Inner properties to calculate y. Indicate relative shift.
+                    this.__index = 0;     // Inner properties to calculate y. Indicate absolute index.
+                }
+
+
+                AttachableLabel.prototype.constructor = AttachableLabel;
+
+                AttachableLabel.prototype.applyShift = function (shiftApplied) {
+                    this.__shift = shiftApplied;
+                    // Shift applied, now calculate the y
+                    this.lRef.calcY = (this.y += shiftApplied * boxHeight);
+                };
+
+                AttachableLabel.prototype.applyDirectIndex = function (newIndex) {
+                    this.__index = newIndex;
+                    // Index changed along the sections of y axis. now calculate the y
+                    this.lRef.calcY = (this.y = _top - (newIndex * boxHeight * -1));
+                };
+
+                /*
+                 * An enum to determine whether the actions (shift / unoccupied position) is in the left side
+                 * or right side.
+                 * >----------------I----------------<
+                 * I is the reference index from which the actions are calculated.
+                 * Anypoint left to I is scale.NEG otherwise scale.POS
+                 */
+                try {
+                    Object.defineProperty (scale, 'POS', {
+                        enumerable : false,
+                        configurable : false,
+                        get : function () { return 1; }
+                    });
+
+                    Object.defineProperty (scale, 'NEG', {
+                        enumerable : false,
+                        configurable : false,
+                        get : function () { return -1; }
+                    });
+                }catch (e) {
+                    // For almighty IE8
+                    scale.POS = 1;
+                    scale.NEG = -1;
+                }
+
+                /*
+                 * Get an array, whose value incrementing from 0 to n-1 for index 0, n-1
+                 * @param: n { int} The number till which increment is needed
+                 * @return: { Array} with value populated from 0 to n-1
+                 */
+                //Array.prototype.inc = function (n) {
+                function inc (n) {
+                    var counter = 0;
+                    while (counter < n) {
+                        this.push (counter++);
+                    }
+                    return this;
+                }
+
+
+                /*
+                 * Calculates the position of nearest undefined (unoccupied) element.
+                 * @param: pos { int} The position from where the reference to be calculated
+                 * @return: { Object} { absValue: { int}, noScaleSide: { scale}}
+                 */
+                //Array.prototype.nearestUndefined = function (pos) {
+                function nearestUndefined (pos) {
+                    var i,
+                        diff,
+                        _scale,
+                        nearest = {},
+                        lowest = Number.POSITIVE_INFINITY;
+
+                    for (i = 0; i < this.length; i++) {
+                        diff = this[i] - pos;
+                        diff < 0 ? (_scale = scale.NEG) : (_scale = scale.POS);
+                        diff = abs (diff);
+
+                        if (diff <= lowest) {
+                            lowest = diff;
+                            nearest.absValue = diff;
+                            nearest.noScaleSide = _scale;
+                        }
+                    }
+
+                    return nearest;
+                }
+
+                /*
+                 * Keeps a matrix of lables plotted virtually alreday, (not actually in DOM) and
+                 * controls the overlapping by shifting or suggesting indexes.
+                 * This class when initialized, is itself an array containg the plotted labels.
+                 * There is an holes array which is of same length and keeps track of the unoccupied position.
+                 * These two are closely related and relate like
+                 *  -------------------------
+                 * |   |   | l1 |   |   | l2 |  Is the occupency matrix itself
+                 *  -------------------------
+                 *  ---------------
+                 * | 0 | 1 | 3 | 4 | Would be the holes.
+                 *  ---------------
+                 * @param: count { int} - size of the matrix. Generally be the size of the sections
+                 */
+                function OccupancyMatrix (count) {
+                    this.holes = inc.call ([], count);
+                }
+
+                OccupancyMatrix.prototype = Object.create (Array.prototype);
+                OccupancyMatrix.prototype.constructor = OccupancyMatrix;
+
+                /*
+                 * Recalculate the state of unoccupied positions freshly.
+                 * Removes the state before it was called
+                 */
+                OccupancyMatrix.prototype.repositionHoles = function () {
+                    var i,
+                        index = 0,
+                        value;
+
+                    this.holes.length = 0;
+                    for (i = 0; i < this.length; i++) {
+                        value = this[i];
+                        !value && (this.holes[index++] = i);
+                    }
+                };
+
+
+                /*
+                 * Possition the labels and attach the shidt information along with calculeatedY on the labels.
+                 * @param: value { int} - value of the labels, inthis case y position of the labels
+                 * @param: index { int} - the section where it would fit normally, calculated based on his value only.
+                 * @param: label { object} - the label object, which is to be attached. label.calcY is injected
+                 *                          after calculating it correctly.
+                 */
+                OccupancyMatrix.prototype.attachShift = function (value, index, label) {
+                    var indexVal,
+                        length = this.length,
+                        nearestUndefIndex,
+                        tempArr,
+                        attachedLabel,
+                        _index,
+                        calibratedIndex,
+                        nearestOffset;
+
+                    if (value === plotOut) {
+                        // Plot outside if label is not visible
+                        label.calcY = plotOut;
+                        return;
+                    }else {
+                        calibratedIndex = (index > (length - 1)) ? length - 1 : index;  // If the plot value is too low
+                        indexVal = this[calibratedIndex];
+                    }
+                    attachedLabel = new AttachableLabel (); // Create a new label which is attachable
+                    attachedLabel.y = value;
+                    attachedLabel.lRef = label;
+
+                    if (!indexVal) {
+                        // If the position is unoccupied, go ahead and occupy it
+                        attachedLabel.applyDirectIndex (calibratedIndex);
+                        this.splice (calibratedIndex, 1, attachedLabel);
+                        this.holes.splice (this.holes.indexOf (calibratedIndex), 1); // Update the occupancy matrix.
+                        return;
+                    }
+
+                    // If position is occupied, the label needs to be opsitioned down or up
+                    nearestOffset = nearestUndefined.call (this.holes, calibratedIndex);
+                    // Get the absolute nearest index
+                    nearestUndefIndex = calibratedIndex + (nearestOffset.absValue * nearestOffset.noScaleSide);
+
+                    if (nearestOffset.noScaleSide === scale.POS) {
+                        // If labels to be attached down the y axis
+                        attachedLabel.applyDirectIndex (nearestUndefIndex);
+                        this.splice (nearestUndefIndex, 1, attachedLabel);
+                        this.holes.splice (this.holes.indexOf (nearestUndefIndex), 1);
+                        return nearestUndefIndex;
+                    }
+
+                    if (nearestOffset.noScaleSide === scale.NEG) {
+                        // If the labels to be aattached is up the y axis we need the shift of sections and
+                        // then put it down
+                        tempArr = this.splice (nearestUndefIndex + 1, this.length - 1); // Section to be shifted
+                        this.pop ();
+                        tempArr.forEach (function (element) {
+                            // Shift the system of attachable lable
+                            element && (element.applyShift (-1));
+                        });
+                        [].push.apply (this, tempArr);
+                        _index = nearestUndefIndex;
+                        // Find the nearest  unoccupied position after shifting the index
+                        while (true) {
+                            if (!this[_index]) {
+                                break;
+                            }
+                            _index++;
+                        }
+                        // This is an dummy insert for recalculation of holes since the whole system has changed.
+                        this.push (0);
+                        this.repositionHoles ();
+                        // After recalculation get the nearest unoocupied position again since there might be better
+                        // place to push the new label
+                        nearestOffset = nearestUndefined.call (this.holes, _index);
+                        _index += (nearestOffset.absValue * nearestOffset.noScaleSide);
+                        attachedLabel.applyDirectIndex (_index);
+                        // Add the label where it was suggested
+                        this.splice (_index, 1, attachedLabel);
+                        this.repositionHoles ();
+                        return this.length - 1;
+                    }
+                };
+
+                try {
+                    Object.defineProperty (result, 'top', {
+                        enumerable : false,
+                        configurable : false,
+                        get : function () { return _top; }
+                    });
+
+                    Object.defineProperty (result, 'bottom', {
+                        enumerable : false,
+                        configurable : false,
+                        get : function () { return _bottom; }
+                    });
+                }catch (e) {
+                    // IE-8
+                    result.top = _top;
+                    result.bottom = _bottom;
+                }
+
+
+                /*
+                 * Initialize the limiting system
+                 * @param height { int} - label height
+                 * @param labelCount { int} - total no of labels
+                 */
+                result.init = function (height, labelCount) {
+                    var i,
+                        defaultValue = 0;
+
+                    boxHeight = height + margin;
+                    _top += (boxHeight / 2);
+                    sections = floor (abs (_top) / boxHeight);
+
+                    if (sections >= labelCount) {
+                        isSpaceEnough = true;
+                    }
+                    oMatrix = new OccupancyMatrix (sections);
+                    for (i = 0; i < sections ; i++) {
+                        oMatrix.push (defaultValue);
+                    }
+                };
+
+                /*
+                 * Occupy one section in the vertical scale
+                 * @param pos { int} - value from data config
+                 * @param label { object} - label object
+                 */
+                result.occupy = function (pos, label) {
+                    var passedSections = floor (abs (_top - pos) / boxHeight);
+                    oMatrix && oMatrix.attachShift (pos, passedSections, label);
+                };
+
+
+                return result;
+
+            }) ();
+        if (labels) {
+            // First set text so that we get correct value of boundingBox
+            labels[0] && labels[0].attr ( {
+                text: numberFormatter.yAxis ('0')
+            });
+
+            labels[0] && verticalLimits.init (labels[0].getBBox ().height, labels.length);
+
+            labels.forEach (function (label, i) {
+                var plot = plots[i],
+                    value = plot.components.data[position] && plot.components.data[position].config.setValue,
+                    labelYPos,
+                    yAxis = plot.config.parentYAxis;
+                if (value === undefined || !plot.visible ||
+                    (yAxis ? (value > sYAxisMaxValue || value < sYAxisMinValue):
+                        (value > pYAxisMaxValue || value < pYAxisMinValue))) {
+                    labelYPos = plotOut;
+                } else {
+                    labelYPos = (yAxis ? (value - sYAxisMinValue) * spvr : (value - pYAxisMinValue) * ppvr ) ;
+                }
+                verticalLimits.occupy (labelYPos, label);
+            });
+        }
+
+
+        labels && labels.forEach (function (label, i) {
+            var plot = plots[i],
+                GUTTER = 10,
+                value = plot.components.data[position] && plot.components.data[position].config.setValue,
+                bBox,
+                labelWidth,
+                halfLabelWidth,
+                adjustedHalfLabelWidth,
+                labelHeight,
+                _xPos,
+                _yPos,
+                text = numberFormatter[plot.config.parentYAxis ? 'sYAxis' : 'yAxis'] (value);
+
+            // Only defined and valid texts are shown on the labels.
+            if (text) {
+                label.attr({
+                    text: text
+                });
+
+                // Now calculate boundingBox and label positions
+                bBox = label.getBBox();
+                labelWidth = bBox && bBox.width;
+                halfLabelWidth = labelWidth && labelWidth * 0.5;
+                adjustedHalfLabelWidth = halfLabelWidth && (halfLabelWidth + GUTTER);
+                labelHeight = bBox && bBox.height;
+                _yPos = label.calcY;
+                _xPos = mathMax (0, mathMin (flooredX, visw));
+                (_yPos !== UNDEFINED && _xPos !== UNDEFINED) && (label.attr ( {
+                    x: _xPos,
+                    y: _yPos,
+                    'text-anchor': (x <= adjustedHalfLabelWidth) && 'start' ||
+                        ((x + adjustedHalfLabelWidth) >= visw) && 'end' || 'middle',
+                    'text-bound': ['rgba(255,255,255,0.8)', 'rgba(0,0,0,0.2)', 1, labelPadding]
+                }));
+            }
+            else {
+                // In case of undefined text labels, the label is transfered to out of visual. One way of hiding it.
+                label.attr({
+                    x: -visw
+                });
+            }
+
+        });
+
+        crossline.positionLabel && crossline.positionLabel.attr ( {
+            x: x + crossline.left,
+            text: crossline.getPositionLabel (position),
+            'text-bound': ['rgba(255,255,255,1)', 'rgba(0,0,0,1)', 1, labelPadding]
+        });
+    };
+
+    CrossLine.prototype.show = function () {
+        if (!this.disabled) {
+            this.hidden = false;
+            this.group.attr ('visibility', 'visible');
+            this.line.attr ('visibility', 'visible');
+            this.positionLabel && this.positionLabel.attr ('visibility', 'visible');
+        }
+    };
+
+    CrossLine.prototype.hide = function () {
+        this.hidden = true;
+        this.group.attr ('visibility', 'hidden');
+        this.line.attr ('visibility', 'hidden');
+        this.positionLabel && this.positionLabel.attr ('visibility', 'hidden');
+    };
+
+    CrossLine.prototype.dispose = function () {
+        var crossline = this,
+            key;
+
+        // delete all the properties in crossline object
+        for (key in crossline) {
+            crossline.hasOwnProperty (key) && (delete crossline[key]);
+        }
+    };
+
+    R.addSymbol ( {
+        pinModeIcon: function (posx, posy, rad) {
+            var x = posx,
+                y = posy,
+                r = rad,
+                r1 = r * 0.5,
+                r2 = r - r1,
+                x1 = x - r,
+                x2 = x + r,
+                x3 = x - r1,
+                x4 = x + r1,
+                x5 = x - 0.5,
+                x6 = x + 0.5,
+                x7 = x6 + 1,
+                x8 = x6 + 1.5,
+                y1 = y - r,
+                y2 = y + r1,
+                y3 = y - r1,
+                y4 = y + r2,
+                y5 = y + r  + 0.5;
+
+            return ['M', x1, y1, 'L', x3, y3, x3, y4, x1, y2, x5, y2, x, y5, x6,
+                y2, x2, y2, x4, y4, x4, y3, x2, y1, x8, y1, x8, y3, x8, y4, x7,
+                y4, x7, y3, x8, y3, x8, y1, 'Z'];
+        },
+
+        zoomOutIcon: function (x, y, radius) {
+
+            var
+            icoX = x - radius * 0.2,
+            icoY = y - radius * 0.2,
+            rad = radius * 0.8,
+            startAngle = R.rad (43),
+            endAngle = R.rad (48), // to prevent cos and sin of start and end from becoming equal on 360 arcs
+            startX = icoX + rad * mathCos (startAngle),
+            startY = icoY + rad * mathSin (startAngle),
+            endX = icoX + rad * mathCos (endAngle),
+            endY = icoY + rad * mathSin (endAngle),
+            handleHeight = radius, // the height of the handle
+            handAngle = R.rad (45),
+            handX1 = startX + handleHeight * mathCos (handAngle),
+            handY1 = startY + handleHeight * mathSin (handAngle),
+            handX2 = endX + handleHeight * mathCos (handAngle),
+            handY2 = endY + handleHeight * mathSin (handAngle),
+            semiW = 2;
+
+            return ['M', startX , startY,
+                'A', rad, rad, 0, 1, 0, endX, endY, 'Z', 'M', startX + 1 , startY + 1 , 'L',
+                handX1, handY1, handX2, handY2, endX + 1,
+                endY + 1, 'Z', 'M', icoX - semiW, icoY, 'L', icoX + semiW,
+                icoY, 'Z'];
+
+        },
+
+        resetIcon: function (x, y, radius) {
+            var r = radius,
+            startX = x - r, startY = y,
+            endAngle = (math.PI / 2 + math.PI) / 2,
+            endX = x + r * mathCos (endAngle),
+            endY = y + r * mathSin (endAngle),
+            arrowLength = r * 2 / 3,
+
+            paths = ['M', startX, startY, 'A',
+                r, r, 0, 1, 1, endX, endY, 'L', endX + arrowLength,
+                endY - 1, endX + 2, endY + arrowLength - 0.5, endX, endY];
+
+            return paths;
+        }
+    });
+
+}]);
+
+
+}));
diff --git a/fusioncharts.gantt.js b/fusioncharts.gantt.js
new file mode 100644
index 0000000..e42965b
--- /dev/null
+++ b/fusioncharts.gantt.js
@@ -0,0 +1,5472 @@
+
+(function (factory) {
+    if (typeof module === 'object' && typeof module.exports !== "undefined") {
+        module.exports = factory;
+    } else {
+        factory(FusionCharts);
+    }
+}(function (FusionCharts) {
+
+/**!
+ * @license FusionCharts JavaScript Library - Gantt Chart
+ * Copyright FusionCharts Technologies LLP
+ * License Information at 
+ *
+ * @version 3.12.0
+ */
+/**
+ * @private
+ * @module fusioncharts.renderer.javascript.gantt
+ * @export fusioncharts.gantt.js
+ */
+FusionCharts.register ('module', ['private', 'modules.renderer.js-gantt', function () {
+    var global = this,
+        lib = global.hcLib,
+        win = global.window,
+        userAgent = win.navigator.userAgent,
+        isIE = /msie/i.test (userAgent) && !win.opera,
+        chartapi = lib.chartAPI,
+        extend2 = lib.extend2,
+        pluck = lib.pluck,
+        pluckNumber = lib.pluckNumber,
+        pluckFontSize = lib.pluckFontSize,
+        getFirstColor = lib.getFirstColor,
+        parseConfiguration = lib.parseConfiguration,
+        setAttribDefs = lib.setAttribDefs,
+        //hasAttribDefs = lib.hasAttribDefs,
+        graphics = lib.graphics,
+        convertColor = graphics.convertColor,
+        getDarkColor = graphics.getDarkColor,
+        parseUnsafeString = lib.parseUnsafeString,
+        getFirstValue = lib.getFirstValue,
+        getValidValue = lib.getValidValue,
+        toPrecision = lib.toPrecision,
+        R = lib.Raphael,
+        chartPaletteStr = lib.chartPaletteStr,
+        componentDispose = lib.componentDispose,
+        schedular = lib.schedular,
+        COMMA = lib.COMMASTRING,
+        setLineHeight = lib.setLineHeight,
+        getDashStyle = lib.getDashStyle,
+        toRaphaelColor = lib.toRaphaelColor,
+        each = lib.each,
+        ROLLOVER = 'DataPlotRollOver',
+        ROLLOUT = 'DataPlotRollOut',
+        attrTypeNum = lib.attrTypeNum,
+        attrTypeBool = lib.attrTypeBool,
+        PXSTRING = 'px',
+        BLANK = '',
+        MAX_MITER_LINEJOIN = 2,
+        dropHash = lib.dropHash,
+        HASHSTRING = lib.HASHSTRING,
+        creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname),
+        TRACKER_FILL = 'rgba(192,192,192,'+ (isIE ? 0.002 : 0.000001) +')', // invisible but clickable
+        UNDEF,
+        // The default value for stroke-dash attribute.
+        DASH_DEF = 'none',
+        mapSymbolName = graphics.mapSymbolName,
+        math = Math,
+        mathMax = math.max,
+        mathMin = math.min,
+        mathAbs = math.abs,
+        pInt = parseInt,
+        toFloat = parseFloat,
+        M = 'M',
+        L = 'L',
+        v = 'v',
+        H = 'H',
+        PX = 'px',
+        ATTRFN = 'attr',
+        BOLD = 'bold',
+        ITALIC = 'italic',
+        NORMAL = 'normal',
+        NONE = 'none',
+        UND_LINE = 'underline',
+        POSITION_BOTTOM = 'bottom',
+        getLightColor = lib.graphics.getLightColor,
+        plotEventHandler = lib.plotEventHandler,
+        hoverTimeout,
+        lastHoverEle,
+        merge = function () { /** @todo refactor dependency */
+            var args = arguments;
+            return lib.extend2 (lib.extend2 (lib.extend2 (lib.extend2 ( { }, args[0]), args[1]), args[2]), args[3]);
+        },
+
+        UNDEFINED,
+
+        defined = function (obj) {
+            return obj !== UNDEFINED && obj !== null;
+        },
+        isPercent = function (srt) {
+            return (/%/g).test (srt);
+        },
+        align = {
+            left: 'start',
+            right: 'end',
+            center: 'middle'
+        },
+        xAlign = {
+            left: 0,
+            right: 1,
+            center: 0.5,
+            'undefined': 0.5
+        },
+        alignGutter = {
+            left: 5,
+            right: -5,
+            center: 0,
+            'undefined': 0
+        },
+        emptyFn = function () {
+        },
+        checkInvalidValue = function () {
+            var i = 0,
+                ii = arguments.length,
+                allValuesValid = false,
+                val;
+
+            for (i = 0; i < ii; i++) {
+                val = arguments[i];
+                if (isNaN(val)) {
+                    return false;
+                }
+                else {
+                    allValuesValid = true;
+                }
+            }
+            return allValuesValid;
+
+        },
+        stylePropValues = {
+            fontWeight: [NORMAL, BOLD],
+            fontStyle: [NORMAL, ITALIC]
+        },
+        extractLabelStyle = function (styleObj) {
+            var style,
+                prop;
+
+            for (prop in styleObj) {
+                if (styleObj[prop] !== UNDEFINED) {
+                    style = style || {};
+                    switch (prop) {
+                        case 'fontWeight':
+                        case 'fontStyle':
+                            style[prop] = stylePropValues[prop][styleObj[prop]];
+                            break;
+                        default:
+                            style[prop] = styleObj[prop];
+                    }
+                }
+            }
+            return style;
+        };
+    // New gantt API
+    chartapi('gantt', {
+        defaultDatasetType: 'task',
+        applicableDSList: {'task': true, 'milestone': true, 'connectors': true},
+        fireGroupEvent: true,
+        aligncaptionwithcanvas: 0,
+        hasInteractiveLegend: false,
+        creditLabel: creditLabel,
+        //init: function (){},
+        //configure: function(){},
+        //manageSpace: function(){},
+        //draw: function(){}
+        _createDatasets : function () {
+            var iapi = this,
+                config = iapi.config,
+                components = iapi.components,
+                dataObj = iapi.jsonData,
+                dataset = [],
+                length,
+                i,
+                datasetStore,
+                datasetStoreLen,
+                datasetObj,
+                datasets,
+                defaultSeriesType = iapi.defaultDatasetType,
+                applicableDSList = iapi.applicableDSList,
+                dsType,
+                DsClass,
+                datasetJSON,
+                JSONData,
+                datasetMap = config.datasetMap || (config.datasetMap = {
+                    'task': [],
+                    'milestone': [],
+                    'connectors': []
+                }),
+                tempMap = {
+                    'task': [],
+                    'milestone': [],
+                    'connectors': []
+                },
+                dsTypeRef,
+                dsRef,
+                prevDataLength,
+                currDataLength,
+                dataType,
+                dsCount = { };
+            if (dataObj.tasks) {
+                dataset = dataset.concat(dataObj.tasks);
+            }
+            if (dataObj.milestones) {
+                dataset = dataset.concat(dataObj.milestones);
+            }
+            if (dataObj.connectors) {
+                dataset = dataset.concat(dataObj.connectors);
+            }
+            length = dataset && dataset.length;
+
+            if(dataset && !(dataset instanceof Array)){
+                dataset = [dataset];
+                length = 1;
+            }
+
+            if (!length) {
+                iapi.setChartMessage();
+            }
+
+            iapi.config.categories = dataObj.categories && dataObj.categories[0].category;
+
+            datasetStore = components.dataset = [];
+            datasetStoreLen = datasetStore.length;
+
+            for (i=0; i currDataLength) {
+                            dsRef.removeData(currDataLength - 1, prevDataLength - currDataLength, false);
+                        }
+                        dsRef.JSONData = datasetJSON;
+                        dsRef.configure();
+                        dsTypeRef.shift();
+                    }
+                }
+            }
+
+            iapi._createLegendItems();
+
+            // Removing unused datasets if any
+            for (dsType in datasetMap) {
+                datasets = datasetMap[dsType];
+                length = datasets.length;
+                if (length) {
+                    for (i = 0; i < length; i++) {
+                        datasetObj = datasets[i];
+                        if (datasetObj) {
+                            // legend.removeItem(datasets[j].legendItemId);
+                            componentDispose.call(datasetObj);
+                        }
+
+                    }
+                }
+            }
+            config.datasetMap = tempMap;
+        },
+
+        configure: function () {
+            var iapi = this,
+                config = iapi.config,
+                jsonData = iapi.jsonData,
+                background = iapi.components.background,
+                bConfig = background.config,
+                chartAttrs = jsonData.chart;
+            this.__setDefaultConfig();
+            chartapi.mscartesian.configure.call(this);
+            config.showBorder = pluckNumber(chartAttrs.showborder, 0);
+            config.borderWidth = config.showBorder ? pluckNumber (chartAttrs.borderthickness, 1) : 0;
+            bConfig.borderWidth = config.borderWidth;
+            config.lastVScrollPosition = UNDEFINED;
+            config.lastHScrollPosition = UNDEFINED;
+
+        },
+        _createLegendItems: function () {
+            var iapi = this,
+                components = iapi.components,
+                dataObj = iapi.jsonData,
+                legend = components.legend,
+                length,
+                i,
+                strokeColor,
+                lightColor,
+                color,
+                fillColor,
+                itemObj,
+                config,
+                prevLen,
+                currLen,
+                legendItem,
+                legendItemsArray = components.legendItems,
+                items = dataObj.legend && dataObj.legend.item || [];
+
+            prevLen = components.legendItems && components.legendItems.length || 0;
+            currLen = items.length;
+            if (prevLen > currLen) {
+                legend.emptyItems(currLen, prevLen - currLen);
+                legendItemsArray && legendItemsArray.splice(currLen, prevLen - currLen);
+            }
+
+            if (!legendItemsArray) {
+                components.legendItems = legendItemsArray = [];
+            }
+
+            for (i = 0, length = items.length; i < length; i++) {
+                itemObj = items[i];
+                legendItem = legendItemsArray[i];
+                color = itemObj.color;
+                strokeColor = getLightColor(color, 60).replace(dropHash, HASHSTRING);
+                lightColor = getLightColor(color, 40);
+
+                fillColor = {
+                    FCcolor: {
+                        color: color + ',' + color + ',' + lightColor + ',' + color + ',' + color,
+                        ratio: '0,70,30',
+                        angle: 270,
+                        alpha: '100,100,100,100,100'
+                    }
+                };
+
+                !legendItem && (legendItem = legendItemsArray[i] = {});
+                config = {
+                    fillColor : toRaphaelColor(fillColor),
+                    strokeColor: toRaphaelColor(strokeColor),
+                    label : itemObj.label,
+                    interactiveLegend: false,
+                    legendItemId: legendItem.legendItemId
+                };
+                legendItemsArray[i].legendItemId = legend.addItems(iapi, emptyFn, config);
+            }
+
+        },
+        _setAxisLimits: function () {
+
+        },
+        _createAxes : function () {
+            var iapi = this,
+                components = iapi.components,
+                TimeAxis = FusionCharts.get ('component', ['axis', 'time']),
+                ProcessAxis = FusionCharts.get ('component', ['axis', 'process']),
+                yAxis,
+                xAxis;
+
+
+            components.yAxis = [];
+            components.xAxis = [];
+            components.yAxis[0] = yAxis = new ProcessAxis ();
+            components.xAxis[0] = xAxis = new TimeAxis ();
+            yAxis.chart = iapi;
+            xAxis.chart = iapi;
+
+            yAxis.init ();
+            xAxis.init ();
+        },
+
+        _feedAxesRawData : function () {
+            var iapi = this,
+                components = iapi.components,
+                colorM = components.colorManager,
+                dataObj = iapi.jsonData,
+                chartAttrs = dataObj.chart,
+                xAxisConf,
+                yAxisConf,
+                is3d = iapi.is3d,
+                palleteString = is3d ? chartPaletteStr.chart3D : chartPaletteStr.chart2D,
+                yAxis,
+                xAxis;
+
+
+            xAxisConf = {
+                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
+                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
+                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
+                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
+                axisBreaks : chartAttrs.xaxisbreaks,
+                axisNamePadding: chartAttrs.xaxisnamepadding,
+                axisValuePadding: chartAttrs.labelpadding,
+                axisNameFont: chartAttrs.xaxisnamefont,
+                axisNameFontSize: chartAttrs.xaxisnamefontsize,
+                axisNameFontColor: chartAttrs.xaxisnamefontcolor,
+                axisNameFontBold: chartAttrs.xaxisnamefontbold,
+                axisNameFontItalic: chartAttrs.xaxisnamefontitalic,
+                axisNameBgColor: chartAttrs.xaxisnamebgcolor,
+                axisNameBorderColor: chartAttrs.xaxisnamebordercolor,
+                axisNameAlpha: chartAttrs.xaxisnamealpha,
+                axisNameFontAlpha: chartAttrs.xaxisnamefontalpha,
+                axisNameBgAlpha: chartAttrs.xaxisnamebgalpha,
+                axisNameBorderAlpha: chartAttrs.xaxisnameborderalpha,
+                axisNameBorderPadding: chartAttrs.xaxisnameborderpadding,
+                axisNameBorderRadius: chartAttrs.xaxisnameborderradius,
+                axisNameBorderThickness: chartAttrs.xaxisnameborderthickness,
+                axisNameBorderDashed: chartAttrs.xaxisnameborderdashed,
+                axisNameBorderDashLen: chartAttrs.xaxisnameborderdashlen,
+                axisNameBorderDashGap: chartAttrs.xaxisnameborderdashgap,
+                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
+                divLineColor: pluck (chartAttrs.vdivlinecolor, colorM.getColor (palleteString.divLineColor)),
+                divLineAlpha: pluck (chartAttrs.vdivlinealpha, is3d ? colorM.getColor('divLineAlpha3D') :
+                    colorM.getColor ('divLineAlpha')),
+                divLineThickness: pluckNumber (chartAttrs.vdivlinethickness, 1),
+                divLineIsDashed: Boolean (pluckNumber (chartAttrs.vdivlinedashed, chartAttrs.vdivlineisdashed, 0)),
+                divLineDashLen: pluckNumber (chartAttrs.vdivlinedashlen, 4),
+                divLineDashGap: pluckNumber (chartAttrs.vdivlinedashgap, 2),
+                showAlternateGridColor: pluckNumber (chartAttrs.showalternatevgridcolor, 0),
+                alternateGridColor: pluck (chartAttrs.alternatevgridcolor, colorM.getColor ('altVGridColor')),
+                alternateGridAlpha: pluck (chartAttrs.alternatevgridalpha, colorM.getColor ('altVGridAlpha')),
+                numDivLines: pluckNumber(chartAttrs.numvdivlines, iapi.numVDivLines),
+                labelFont: chartAttrs.labelfont,
+                labelFontSize: chartAttrs.labelfontsize,
+                labelFontColor: chartAttrs.labelfontcolor,
+                labelFontAlpha: chartAttrs.labelalpha,
+                labelFontBold: chartAttrs.labelfontbold,
+                labelFontItalic: chartAttrs.labelfontitalic,
+                maxLabelHeight : chartAttrs.maxlabelheight,
+                axisName: chartAttrs.xaxisname,
+                axisMinValue: chartAttrs.xaxisminvalue,
+                axisMaxValue: chartAttrs.xaxismaxvalue,
+                setAdaptiveMin: chartAttrs.setadaptivexmin,
+                adjustDiv: chartAttrs.adjustvdiv,
+                labelDisplay: chartAttrs.labeldisplay,
+                showLabels: chartAttrs.showlabels,
+                rotateLabels: chartAttrs.rotatelabels,
+                slantLabel: pluckNumber (chartAttrs.slantlabels, chartAttrs.slantlabel),
+                labelStep: pluckNumber (chartAttrs.labelstep, chartAttrs.xaxisvaluesstep),
+                showAxisValues: pluckNumber (chartAttrs.showxaxisvalues,  chartAttrs.showxaxisvalue),
+                showLimits: pluckNumber(chartAttrs.showvlimits, iapi.showvlimits),
+                showDivLineValues: pluckNumber (chartAttrs.showvdivlinevalues, chartAttrs.showvdivlinevalues),
+                //showZeroPlane: chartAttrs.showvzeroplane,
+                zeroPlaneColor: chartAttrs.vzeroplanecolor,
+                zeroPlaneThickness: chartAttrs.vzeroplanethickness || 2,
+                zeroPlaneAlpha: chartAttrs.vzeroplanealpha,
+                showZeroPlaneValue: chartAttrs.showvzeroplanevalue,
+                trendlineColor: chartAttrs.trendlinecolor,
+                trendlineToolText: chartAttrs.trendlinetooltext,
+                trendlineThickness: chartAttrs.trendlinethickness,
+                trendlineAlpha: chartAttrs.trendlinealpha,
+                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
+                showAxisLine: pluckNumber (chartAttrs.showxaxisline, chartAttrs.showaxislines,
+                    chartAttrs.drawAxisLines, 0),
+                axisLineThickness: pluckNumber (chartAttrs.xaxislinethickness, chartAttrs.axislinethickness, 1),
+                axisLineAlpha: pluckNumber (chartAttrs.xaxislinealpha, chartAttrs.axislinealpha, 100),
+                axisLineColor: pluck (chartAttrs.xaxislinecolor, chartAttrs.axislinecolor, '#000000')
+            };
+            yAxisConf = {
+                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
+                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
+                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
+                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
+                axisBreaks : chartAttrs.yaxisbreaks,
+                axisNamePadding: chartAttrs.yaxisnamepadding,
+                axisValuePadding: chartAttrs.yaxisvaluespadding,
+                axisNameFont: chartAttrs.yaxisnamefont,
+                axisNameFontSize: chartAttrs.yaxisnamefontsize,
+                axisNameFontColor: chartAttrs.yaxisnamefontcolor,
+                axisNameFontBold: chartAttrs.yaxisnamefontbold,
+                axisNameFontItalic: chartAttrs.yaxisnamefontitalic,
+                axisNameBgColor: chartAttrs.yaxisnamebgcolor,
+                axisNameBorderColor: chartAttrs.yaxisnamebordercolor,
+                axisNameAlpha: chartAttrs.yaxisnamealpha,
+                axisNameFontAlpha: chartAttrs.yaxisnamefontalpha,
+                axisNameBgAlpha: chartAttrs.yaxisnamebgalpha,
+                axisNameBorderAlpha: chartAttrs.yaxisnameborderalpha,
+                axisNameBorderPadding: chartAttrs.yaxisnameborderpadding,
+                axisNameBorderRadius: chartAttrs.yaxisnameborderradius,
+                axisNameBorderThickness: chartAttrs.yaxisnameborderthickness,
+                axisNameBorderDashed: chartAttrs.yaxisnameborderdashed,
+                axisNameBorderDashLen: chartAttrs.yaxisnameborderdashlen,
+                axisNameBorderDashGap: chartAttrs.yaxisnameborderdashgap,
+                axisNameWidth: chartAttrs.yaxisnamewidth,
+                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
+                rotateAxisName: pluckNumber (chartAttrs.rotateyaxisname, 1),
+                axisName: chartAttrs.yaxisname,
+                divLineColor: pluck (chartAttrs.divlinecolor, colorM.getColor (palleteString.divLineColor)),
+                divLineAlpha: pluck (chartAttrs.divlinealpha, is3d ? colorM.getColor('divLineAlpha3D') :
+                    colorM.getColor ('divLineAlpha')),
+                divLineThickness: pluckNumber (chartAttrs.divlinethickness, 1),
+                divLineIsDashed: Boolean (pluckNumber (chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
+                divLineDashLen: pluckNumber (chartAttrs.divlinedashlen, 4),
+                divLineDashGap: pluckNumber (chartAttrs.divlinedashgap, 2),
+                showAlternateGridColor: pluckNumber (chartAttrs.showalternatehgridcolor, 1),
+                alternateGridColor: pluck (chartAttrs.alternatehgridcolor, colorM.getColor ('altHGridColor')),
+                alternateGridAlpha: pluck (chartAttrs.alternatehgridalpha, colorM.getColor ('altHGridAlpha')),
+                numDivLines: pluckNumber(chartAttrs.numdivlines, iapi.numDivLines),
+                axisMinValue: chartAttrs.yaxisminvalue,
+                axisMaxValue: chartAttrs.yaxismaxvalue,
+                setAdaptiveMin: chartAttrs.setadaptiveymin,
+                adjustDiv: chartAttrs.adjustdiv,
+                labelStep: chartAttrs.yaxisvaluesstep,
+                showAxisValues: pluckNumber (chartAttrs.showyaxisvalues, chartAttrs.showyaxisvalue),
+                showLimits: pluckNumber(chartAttrs.showlimits, iapi.showLimits),
+                showDivLineValues: pluckNumber (chartAttrs.showdivlinevalues, chartAttrs.showdivlinevalue),
+                //showZeroPlane: chartAttrs.showzeroplane,
+                zeroPlaneColor: chartAttrs.zeroplanecolor,
+                zeroPlaneThickness: chartAttrs.zeroplanethickness || 2,
+                zeroPlaneAlpha: chartAttrs.zeroplanealpha,
+                showZeroPlaneValue: chartAttrs.showzeroplanevalue,
+                trendlineColor: chartAttrs.trendlinecolor,
+                trendlineToolText: chartAttrs.trendlinetooltext,
+                trendlineThickness: chartAttrs.trendlinethickness,
+                trendlineAlpha: chartAttrs.trendlinealpha,
+                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
+                showAxisLine: pluckNumber (chartAttrs.showyaxisline, chartAttrs.showaxislines,
+                    chartAttrs.drawAxisLines, 0),
+                axisLineThickness: pluckNumber (chartAttrs.yaxislinethickness, chartAttrs.axislinethickness, 1),
+                axisLineAlpha: pluckNumber (chartAttrs.yaxislinealpha, chartAttrs.axislinealpha, 100),
+                axisLineColor: pluck (chartAttrs.yaxislinecolor, chartAttrs.axislinecolor, '#000000')
+            };
+
+            xAxisConf.vtrendlines = dataObj.trendlines;
+            //yAxisConf.trendlines = dataObj.trendlines;
+
+            yAxis = components.yAxis[0];
+            xAxis = components.xAxis[0];
+
+            yAxis.setCommonConfigArr (yAxisConf, true, true, false);
+            xAxis.setCommonConfigArr (xAxisConf, false, false, true);
+            yAxis.configure();
+            xAxis.configure();
+        },
+
+        _setCategories: function(){
+            var iapi = this,
+                components = iapi.get('components'),
+                dataObj = iapi.jsonData,
+                xAxis = components.xAxis,
+                yAxis = components.yAxis,
+                processes = dataObj.processes,
+                dataTable = dataObj.datatable,
+                categories = dataObj.categories;
+
+            yAxis[0].setAxisPadding(0.5, 0.5);
+            yAxis[0].setProcess(processes);
+            yAxis[0].setDataTable(dataTable);
+            xAxis[0].setCategory(categories);
+
+        },
+        _setAxisValuePadding: function () {
+            var iapi = this,
+                datasets = iapi.components.dataset,
+                len = datasets.length,
+                i,
+                datasetObj,
+                axisPaddingLeft = -Infinity,
+                axisPaddingRight = -Infinity,
+                //yAxis = iapi.components.yAxis,
+                paddingObj;
+            for (i = 0; i < len; i++) {
+                datasetObj = datasets[i];
+                paddingObj = datasetObj.getAxisValuePadding && datasetObj.getAxisValuePadding() || {};
+                axisPaddingLeft = mathMax(axisPaddingLeft, paddingObj.left || -Infinity);
+                axisPaddingRight = mathMax(axisPaddingRight, paddingObj.right || -Infinity);
+            }
+            if (axisPaddingLeft === -Infinity) {
+                axisPaddingLeft = 0;
+            }
+            if (axisPaddingRight === -Infinity) {
+                axisPaddingRight = 0;
+            }
+            //xAxis && xAxis[0].setAxisPadding(axisPaddingLeft, axisPaddingLeft);
+        },
+
+        _spaceManager: function () {
+            // todo marge _allocateSpace and _spacemanager
+            var availableWidth,
+                availableHeight,
+                iapi = this,
+                config = iapi.config,
+                components = iapi.components,
+                xAxis = components.xAxis && components.xAxis[0],
+                yAxis = components.yAxis && components.yAxis[0],
+                legend = components.legend,
+                legendPosition = legend.config.legendPos,
+                allottedSpace,
+                ganttWidthPercent = config.ganttwidthpercent,
+                processWidthPercent = 100 - (ganttWidthPercent || 67),
+                chartBorderWidth = config.borderWidth,
+                spaceUsed,
+                processHeight,
+                totalWidth = 0,
+                totalHeight = 0;
+
+            iapi._resetViewPortConfig && iapi._resetViewPortConfig();
+            xAxis.resetTransletAxis();
+            yAxis.resetTransletAxis();
+            iapi._allocateSpace ( {
+                top : chartBorderWidth,
+                bottom : chartBorderWidth,
+                left : chartBorderWidth,
+                right : chartBorderWidth
+            });
+            iapi._allocateSpace(iapi._manageActionBarSpace &&
+                iapi._manageActionBarSpace(config.availableHeight * 0.225) || {});
+
+            availableHeight = (legendPosition === POSITION_BOTTOM) ? config.canvasHeight * 0.6 :
+                config.canvasWidth * 0.6;
+
+            // a space manager that manages the space for the tools as well as the captions.
+            iapi._manageChartMenuBar(availableHeight);
+
+
+            if (legendPosition === 'right') {
+                allottedSpace = config.canvasWidth * 0.3;
+            }
+            else {
+                allottedSpace = config.canvasHeight * 0.3;
+            }
+
+
+
+            //Manage space for legend
+            iapi._manageLegendSpace(allottedSpace);
+
+            config.actualCanvasTop = config.canvasTop;
+            config.actualCanvasLeft = config.canvasLeft;
+            //****** Manage space for y axis here y axis is the process column
+            availableWidth = config.canvasWidth * (processWidthPercent / 100);
+
+            spaceUsed = yAxis.placeAxis (availableWidth);
+
+            totalWidth += (spaceUsed.left || 0) + (spaceUsed.right || 0);
+
+            yAxis && iapi._allocateSpace (spaceUsed);
+
+            availableHeight = (legendPosition === POSITION_BOTTOM) ? config.canvasHeight * 0.6 :
+                config.canvasWidth * 0.6;
+
+            availableHeight = config.canvasHeight * 0.6;
+
+            spaceUsed = xAxis.placeAxis (availableHeight);
+
+            totalHeight += (spaceUsed.top || 0);
+
+            config.totalWidth = totalWidth;
+            config.totalHeight = totalHeight;
+
+            xAxis && iapi._allocateSpace (spaceUsed);
+
+            processHeight = yAxis && yAxis.setProcessHeight();
+            yAxis.setAxisConfig({
+                processTotalHeight : processHeight
+            });
+        },
+
+        _drawCanvas: function () {
+            var iapi = this,
+                jsonData = iapi.jsonData,
+                chartAttrs = jsonData.chart,
+                components = iapi.components,
+                chartConfig = iapi.config,
+                chartGraphics = iapi.graphics,
+                paper = components.paper,
+                canvas = components.canvas,
+                graphics = canvas.graphics,
+                config = canvas.config,
+                clip = config.clip = { },
+                canvasBorderElement = graphics.canvasBorderElement,
+                animationObj = iapi.get('config', 'animationObj'),
+                animationDuration = animationObj.duration,
+                dummyAnimElem = animationObj.dummyObj,
+                dummyAnimObj = animationObj.animObj,
+                animType = animationObj.animType,
+                canvasElement = graphics.canvasElement,
+                canvasLeft = chartConfig.actualCanvasLeft,
+                canvasTop = chartConfig.actualCanvasTop,
+                actualCanvasWidth = chartConfig.canvasWidth,
+                actualCanvasHeight = chartConfig.canvasHeight,
+                actualCanvasLeft = chartConfig.canvasLeft,
+                actualCanvasTop = chartConfig.canvasTop,
+                canvasWidth = chartConfig.canvasWidth + chartConfig.totalWidth,
+                canvasHeight = chartConfig.canvasHeight + chartConfig.totalHeight,
+                canvasGroup = chartGraphics.canvasGroup,
+                canvasBorderRadius = config.canvasBorderRadius,
+                canvasBorderWidth = config.canvasBorderWidth,
+                borderWHlf = canvasBorderWidth * 0.5,
+                canvasBorderColor = config.canvasBorderColor,
+                canBGColor = config.canBGColor,
+                shadow = config.shadow,
+                canvasBgColor,
+                attr,
+                showCanvasBg = config.showCanvasBG = Boolean (pluckNumber (chartAttrs.showcanvasbg, 1)),
+                shadowOnCanvasFill = config.shadowOnCanvasFill,
+                showCanvasBorder = config.showCanvasBorder;
+
+            canvasBgColor = canBGColor;
+            attr = {
+                x: canvasLeft - borderWHlf,
+                y: canvasTop - borderWHlf,
+                width: canvasWidth + canvasBorderWidth,
+                height: canvasHeight + canvasBorderWidth,
+                r: canvasBorderRadius,
+                'stroke-width': canvasBorderWidth,
+                stroke: canvasBorderColor,
+                'stroke-linejoin': canvasBorderWidth > MAX_MITER_LINEJOIN ? 'round' : 'miter'
+            };
+
+            if (showCanvasBorder) {
+                if (!canvasBorderElement) {
+                    graphics.canvasBorderElement = paper.rect (attr, canvasGroup)
+                    .shadow(shadow);
+                }
+                else {
+                    canvasBorderElement.show();
+                    canvasBorderElement.animateWith(dummyAnimElem, dummyAnimObj, {
+                            x: canvasLeft - borderWHlf,
+                            y: canvasTop - borderWHlf,
+                            width: canvasWidth + canvasBorderWidth,
+                            height: canvasHeight + canvasBorderWidth,
+                            r: canvasBorderRadius
+                        }, animationDuration, animType);
+                    canvasBorderElement.attr ({
+                        'stroke-width': canvasBorderWidth,
+                        stroke: canvasBorderColor,
+                        'stroke-linejoin': canvasBorderWidth > MAX_MITER_LINEJOIN ? 'round' : 'miter'
+                    });
+                    canvasBorderElement.shadow(shadow);
+                }
+            }
+            else {
+                canvasBorderElement && canvasBorderElement.hide();
+            }
+
+
+            //create a clip-rect to clip canvas for later use
+            clip['clip-canvas'] = [
+                mathMax (0, actualCanvasLeft),
+                mathMax (0, actualCanvasTop),
+                mathMax (1, actualCanvasWidth),
+                mathMax (1, actualCanvasHeight)
+            ];
+            clip['clip-canvas-init'] = [
+                mathMax (0, actualCanvasLeft),
+                mathMax (0, actualCanvasTop),
+                1,
+                mathMax (1, actualCanvasHeight)
+            ];
+
+            if (showCanvasBg) {
+                attr = {
+                    x: canvasLeft,
+                    y: canvasTop,
+                    width: canvasWidth,
+                    height: canvasHeight,
+                    r: canvasBorderRadius,
+                    'stroke-width': 0,
+                    'stroke': NONE,
+                    fill: toRaphaelColor (canvasBgColor)
+                };
+
+                if (!canvasElement) {
+                    graphics.canvasElement = paper.rect (attr, canvasGroup)
+                    .shadow (shadowOnCanvasFill);
+                }
+                else {
+                    canvasElement.show();
+                    if (animationDuration) {
+                        canvasElement.animateWith(dummyAnimElem, dummyAnimObj, {
+                            x: canvasLeft,
+                            y: canvasTop,
+                            width: canvasWidth,
+                            height: canvasHeight,
+                            r: canvasBorderRadius
+                        }, animationDuration, animType);
+                    }
+                    canvasElement.attr (attr);
+                    canvasElement.shadow (shadowOnCanvasFill);
+                }
+            }
+            else {
+                canvasElement && canvasElement.hide();
+            }
+        },
+        _postSpaceManagement : function () {
+            var iapi = this,
+                config = iapi.config,
+                components = iapi.components,
+                xAxis = components.xAxis && components.xAxis[0],
+                yAxis = components.yAxis && components.yAxis[0],
+                processTotalHeight = yAxis.getAxisConfig('processTotalHeight'),
+                canvasHeight = config.canvasHeight,
+                legend = components.legend,
+                xDepth = config.xDepth,
+                canvasConfig = components.canvas.config,
+                canvasBorderWidth = canvasConfig.canvasBorderWidth,
+                canvasPadding = canvasConfig.canvasPadding,
+                vScrollBar = components.vScrollBar,
+                conf = vScrollBar.userConf,
+                vScrollWidth,
+                vScrollEnabled,
+                canvasPaddingLeft = canvasConfig.canvasPaddingLeft,
+                canvasPaddingRight = canvasConfig.canvasPaddingRight,
+                gPaneDuration = Number(config.ganttpaneduration),
+                gPaneDurationUnit = config.ganttpanedurationunit,
+                hProcessScrollBar = components.hProcessScrollBar,
+                hScrollBar = components.hScrollBar,
+                hScrollBarHeight = hScrollBar && hScrollBar.userConf.height || 0,
+                hProcessScrollHeight = hProcessScrollBar && hProcessScrollBar.userConf.height || 0,
+                totalProcessWidth,
+                totalVisiblelProcessWidth,
+                hScrollEnabled,
+                hProcessScrollEnabled,
+                maxHScrollBarHeight,
+                actualCanvasHeight,
+                axisLength;
+
+            if (gPaneDuration !== UNDEF && gPaneDurationUnit !== UNDEF) {
+                hScrollEnabled = true;
+            }
+            else {
+                hScrollEnabled = false;
+            }
+            totalProcessWidth = yAxis.getAxisConfig('totalWidth');
+            totalVisiblelProcessWidth = yAxis.getAxisConfig('totalVisiblelWidth');
+            if (totalProcessWidth > totalVisiblelProcessWidth) {
+                hProcessScrollEnabled = true;
+            }
+            else {
+                hProcessScrollEnabled = false;
+            }
+
+            maxHScrollBarHeight = mathMax(hProcessScrollEnabled ? hProcessScrollHeight : 0,
+                hScrollEnabled ? hScrollBarHeight : 0);
+            actualCanvasHeight = canvasHeight - maxHScrollBarHeight;
+
+            // Need to call manageScrollerPosition here for knowing whether vertical scroll bar is there
+            if (processTotalHeight > actualCanvasHeight) {
+                vScrollEnabled = true;
+            }
+
+            vScrollWidth = vScrollEnabled ? conf.width || 0 : 0;
+
+            axisLength = config.canvasWidth - (xDepth || 0) - mathMax(canvasPaddingLeft, canvasPadding) -
+                mathMax(canvasPaddingRight, canvasPadding) - vScrollWidth;
+            xAxis && xAxis.setAxisDimention ( {
+                x : config.canvasLeft + (xDepth || 0) + mathMax(canvasPaddingLeft, canvasPadding),
+                y : config.canvasTop - (config.shift || 0),
+                opposite : config.canvasBottom + canvasBorderWidth,
+                axisLength : axisLength
+            });
+
+            config.currentCanvasWidth = axisLength;
+
+            yAxis && yAxis.setAxisDimention ( {
+                x : config.canvasLeft - (config.shift || 0),
+                y : config.canvasTop,
+                opposite : config.canvasRight + canvasBorderWidth,
+                axisLength : config.canvasHeight
+            });
+            iapi._manageScrollerPosition();
+            legend.postSpaceManager();
+        },
+
+        _resetViewPortConfig: function () {
+            var iapi = this;
+
+            iapi.config.viewPortConfig = {
+                scaleX: 1,
+                scaleY: 1,
+                x: 0,
+                y: 0
+            };
+        },
+        _manageScrollerPosition: function () {
+            var iapi = this,
+                config = iapi.config,
+                chartComponents = iapi.components,
+                availableHeight,
+                availableWidth,
+                hScrollEnabled,
+                vScrollEnabled,
+                hScrollBar = chartComponents.hScrollBar,
+                vScrollBar = chartComponents.vScrollBar,
+                hProcessScrollBar = chartComponents.hProcessScrollBar,
+                totalWidth = config.totalWidth || 0,
+                totalHeight = config.totalHeight || 0,
+                scrollDimensions;
+            iapi._setAxisScale && iapi._setAxisScale();
+            vScrollEnabled = config.vScrollEnabled;
+            hScrollEnabled = config.hScrollEnabled;
+            //manage space for the scroll bar.
+            // fetch the availble space for the canvas area after other space management is over
+            availableHeight = config.canvasHeight;
+            availableWidth = config.canvasWidth;
+            scrollDimensions = hScrollBar.getLogicalSpace();
+            //allocate space for toolBox and set the chart configurations.
+            // shift denotes the amount of shift required by the x-axis
+            config.hScrollHeight = hScrollEnabled === false ? 0 : scrollDimensions.height + vScrollBar.conf.padding;
+            scrollDimensions = vScrollBar.getLogicalSpace();
+
+            config.vScrollWidth = vScrollEnabled !== false ?
+                scrollDimensions.width + vScrollBar.conf.padding : 0;
+
+            scrollDimensions = hProcessScrollBar.getLogicalSpace();
+            config.hProcessScrollHeight = config.hProcessScrollEnabled ?
+                scrollDimensions.height + hProcessScrollBar.conf.padding : 0;
+
+            totalHeight += mathMax(config.hProcessScrollHeight, config.hScrollHeight);
+
+            iapi._allocateSpace({
+                bottom: mathMax(config.hProcessScrollHeight, config.hScrollHeight)
+            });
+            config.totalWidth = totalWidth;
+            config.totalHeight = totalHeight;
+
+        },
+        updateManager: function (pos, isHorizontal) {
+            var iapi = this,
+                config = iapi.config,
+                viewPortConfig = iapi.config.viewPortConfig,
+                scaleX = viewPortConfig.scaleX,
+                xAxis = iapi.components.xAxis[0],
+                yAxis = iapi.components.yAxis[0],
+                datasetLayer = iapi.graphics.datasetGroup,
+                dataLabelsLayer = iapi.graphics.datalabelsGroup,
+                trackerGroup = iapi.graphics.trackerGroup,
+                xOffset = config.xOffset || 0,
+                yOffset = config.yOffset || 0,
+                canvasHeight = config.canvasHeight,
+                currentCanvasWidth = config.currentCanvasWidth,
+                scaleY = viewPortConfig.scaleY,
+                transformAttr;
+
+            if (isHorizontal) {
+                config.lastHScrollPosition = pos;
+            }
+            else {
+                config.lastVScrollPosition = pos;
+            }
+            if (isHorizontal) {
+                xOffset = config.xOffset = (currentCanvasWidth * (scaleX - 1) * pos);
+                viewPortConfig.x =  xOffset / scaleX;
+                xAxis.translateAxis(-xOffset, undefined);
+            }
+            else {
+                yOffset = config.yOffset = (canvasHeight * (scaleY - 1) * pos);
+                viewPortConfig.y = yOffset / scaleY;
+                yAxis.translateAxis(undefined, -yOffset);
+            }
+            transformAttr = 't' + -xOffset + ', ' + -yOffset;
+            datasetLayer.attr({
+                transform: transformAttr
+            });
+
+
+            dataLabelsLayer.attr({
+                transform: transformAttr
+            });
+            trackerGroup.attr({
+                transform: transformAttr
+            });
+        },
+        _setAxisScale: function () {
+            var iapi = this,
+                components = iapi.components,
+                config = iapi.config,
+                xAxis = components.xAxis[0],
+                limits = xAxis.getLimit(),
+                max = limits.max,
+                min = limits.min,
+                minDt = new Date(min),
+                yAxis = components.yAxis[0],
+                numberFormatter = iapi.components.numberFormatter,
+                gPaneDuration = Number(config.ganttpaneduration),
+                scrollOptions = config.scrollOptions || (config.scrollOptions = {}),
+                gPaneDurationUnit = config.ganttpanedurationunit,
+                // scrollToDate = config.scrolltodate,
+                scaleX,
+                ms = max - min,
+                canvasHeight = config.canvasHeight,
+                scrollToDate = config.scrolltodate,
+                canvasWidth = config.canvasWidth,
+                datems,
+                canvasLeft = config.canvasLeft,
+                hProcessScrollBar = components.hProcessScrollBar,
+                hScrollBar = components.hScrollBar,
+                maxHScrollBarHeight,
+                hScrollBarHeight = hScrollBar && hScrollBar.userConf.height || 0,
+                hProcessScrollHeight = hProcessScrollBar && hProcessScrollBar.userConf.height || 0,
+                datePixel,
+                totalWidth = xAxis.getPixel(max) - canvasLeft,
+                processTotalHeight = yAxis.getAxisConfig('processTotalHeight'),
+                totalVisiblelProcessWidth,
+                totalProcessWidth,
+                actualCanvasHeight,
+                width,
+                units;
+            if (gPaneDuration === UNDEF || gPaneDurationUnit === UNDEF) {
+                config.hScrollEnabled = false;
+            }
+            else {
+
+                switch (gPaneDurationUnit) {
+                    case 'y' :
+                        minDt.setFullYear(minDt.getFullYear() + gPaneDuration);
+                        break;
+                    case 'm' :
+                        minDt.setMonth(minDt.getMonth() + gPaneDuration);
+                        break;
+                    case 'd' :
+                        minDt.setDate(minDt.getDate() + gPaneDuration);
+                        break;
+                    case 'h' :
+                        minDt.setHours(minDt.getHours() + gPaneDuration);
+                        break;
+                    case 'mn' :
+                        minDt.setMinutes(minDt.getMinutes() + gPaneDuration);
+                        break;
+                    default :
+                        minDt.setSeconds(minDt.getSeconds() + gPaneDuration);
+                        break;
+                }
+                minDt = minDt.getTime();
+                width = xAxis.getPixel(minDt) - canvasLeft;
+                config.hScrollEnabled = true;
+                //update the scaleX for the axis.
+                config.viewPortConfig.scaleX = scaleX = totalWidth / width;
+                //parse the scroll properties
+                scrollOptions.horizontalVxLength = (ms / units) * gPaneDuration;
+                if (scrollToDate) {
+                    datems = numberFormatter.getDateValue (scrollToDate).ms;
+                    datePixel = xAxis.getPixel(datems);
+                    config.viewPortConfig.x = (mathMin(datePixel - canvasLeft, canvasWidth * (scaleX - 1)) / scaleX);
+                    // console.log(config.viewPortConfig.x);
+                }
+
+            }
+
+            totalProcessWidth = yAxis.getAxisConfig('totalWidth');
+            totalVisiblelProcessWidth = yAxis.getAxisConfig('totalVisiblelWidth');
+            if (totalProcessWidth > totalVisiblelProcessWidth) {
+                config.hProcessScrollEnabled = true;
+            }
+            else {
+                config.hProcessScrollEnabled = false;
+            }
+
+
+            maxHScrollBarHeight = mathMax(config.hProcessScrollEnabled ? hProcessScrollHeight : 0,
+                config.hScrollEnabled ? hScrollBarHeight : 0);
+            actualCanvasHeight = canvasHeight - maxHScrollBarHeight;
+            if (Math.floor(processTotalHeight) > actualCanvasHeight) {
+                config.viewPortConfig.scaleY = processTotalHeight / actualCanvasHeight;
+                config.vScrollEnabled = true;
+            }
+            else {
+                config.vScrollEnabled = false;
+            }
+        },
+
+
+        drawScrollBar: function () {
+            var iapi = this,
+                config = iapi.config,
+                components = iapi.components,
+                xAxis = components.xAxis[0],
+                axisConfig = xAxis.config,
+                axisRange = xAxis.config.axisRange,
+                viewPortConfig = config.viewPortConfig,
+                scrollOptions = config.scrollOptions || (config.scrollOptions = {}),
+                max = axisRange.max,
+                min = axisRange.min,
+                vxLength = scrollOptions.horizontalVxLength,
+                hScrollBar = components.hScrollBar,
+                hScrollNode = hScrollBar.node,
+                vScrollBar = components.vScrollBar,
+                vScrollNode = vScrollBar.node,
+                hProcessScrollBar = components.hProcessScrollBar,
+                hProcessScrollNode = hProcessScrollBar.node,
+                useVerticalScrollBar = config.useverticalscrolling,
+                scrollableLength = max - min,
+                canvasRight = config.canvasRight,
+                scaleX = viewPortConfig.scaleX,
+                scaleY = viewPortConfig.scaleY,
+                canvasLeft,
+                canvasTop,
+                canvasHeight,
+                canvasWidth,
+                canvasConfig,
+                canvasBorderWidth,
+                aXisLineWidth,
+                aXisLineStartExtension,
+                aXisLineEndExtension,
+                scrollRatio,
+                vScrollRatio,
+                windowedCanvasHeight,
+                fullCanvasHeight,
+                vScrollEnabled = config.vScrollEnabled,
+                // canvasHeight = config.canvasHeight,
+                yAxis = components.yAxis[0],
+                windowedCanvasWidth,
+                vScrollWidth = vScrollEnabled ? vScrollBar.conf.width : 0,
+                fullCanvasWidth,
+                totalProcessWidth,
+                totalVisiblelProcessWidth,
+                graphics = iapi.graphics,
+                scrollBarParentGroup,
+                paper = components.paper;
+
+            canvasLeft = config.canvasLeft;
+            canvasTop = config.canvasTop;
+            canvasHeight = config.canvasHeight;
+            canvasWidth = config.canvasWidth;
+            canvasConfig = components.canvas.config;
+            canvasBorderWidth = canvasConfig.canvasBorderWidth;
+            aXisLineWidth = pluckNumber(canvasBorderWidth, (axisConfig.showLine ? axisConfig.axisLineThickness : 0));
+            aXisLineStartExtension = pluckNumber(canvasBorderWidth, axisConfig.lineStartExtension);
+            aXisLineEndExtension = pluckNumber(canvasBorderWidth, axisConfig.lineEndExtension);
+            scrollOptions.viewPortMin = min;
+            scrollOptions.viewPortMax = max;
+            scrollRatio = (scrollOptions.scrollRatio = vxLength / scrollableLength);
+            windowedCanvasWidth = scrollOptions.windowedCanvasWidth = xAxis.getAxisPosition(vxLength);
+            fullCanvasWidth = scrollOptions.fullCanvasWidth =
+                xAxis.getAxisPosition(max - min) - windowedCanvasWidth;
+
+            fullCanvasHeight = scrollOptions.fullCanvasHeight = yAxis.getAxisConfig('processTotalHeight');
+            windowedCanvasHeight = scrollOptions.windowedCanvasHeight = canvasHeight;
+            totalProcessWidth = yAxis.getAxisConfig('totalWidth');
+            totalVisiblelProcessWidth = yAxis.getAxisConfig('totalVisiblelWidth');
+
+            vScrollRatio = 1 / scaleY;
+            scrollBarParentGroup = graphics.scrollBarParentGroup;
+            if (!scrollBarParentGroup) {
+                scrollBarParentGroup = graphics.scrollBarParentGroup = paper.group('scrollBarParentGroup',
+                   graphics.parentGroup).insertBefore(graphics.datalabelsGroup);
+            }
+            //draw the scroller element
+            // todo padding needs to be included.
+            if (config.hScrollEnabled !== false) {
+                hScrollBar.draw (canvasLeft,
+                    canvasTop + canvasHeight,{
+                        width: canvasWidth - vScrollWidth,
+                        scrollRatio: 1 / scaleX,
+                        scrollPosition: (viewPortConfig.x  * scaleX) / (canvasWidth * (scaleX - 1)),
+                        roundEdges: canvasConfig.isRoundEdges,
+                        fullCanvasWidth: fullCanvasWidth,
+                        windowedCanvasWidth: windowedCanvasWidth,
+                        parentLayer: scrollBarParentGroup
+                    });
+
+                // attach the callback for raising event only for it is a new scroll node.
+                !hScrollNode && (function () {
+                    var prevPos;
+                    R.eve.on('raphael.scroll.start.' + hScrollBar.node.id, function (pos) {
+                        prevPos = pos;
+                        global.raiseEvent('scrollstart', {
+                            scrollPosition: pos
+                        }, iapi.chartInstance);
+                    });
+
+                    R.eve.on('raphael.scroll.end.' + hScrollBar.node.id, function (pos) {
+                        global.raiseEvent('scrollend', {
+                            prevScrollPosition: prevPos,
+                            scrollPosition: pos
+                        }, iapi.chartInstance);
+                    });
+                }());
+            }
+            else {
+                hScrollBar && hScrollBar.node && hScrollBar.node.hide();
+            }
+
+            if (vScrollEnabled !== false && useVerticalScrollBar) {
+                vScrollBar.draw (canvasRight - vScrollWidth, canvasTop, {
+                    height: canvasHeight,
+                    scrollRatio: vScrollRatio,
+                    roundEdges: canvasConfig.isRoundEdges,
+                    fullCanvasWidth: fullCanvasHeight,
+                    windowedCanvasWidth: windowedCanvasHeight,
+                    parentLayer: scrollBarParentGroup
+                });
+
+                // attach the callback for raising event only for it is a new scroll node.
+                !vScrollNode && (function () {
+                    var prevPos;
+                    R.eve.on('raphael.scroll.start.' + vScrollBar.node.id, function (pos) {
+                        prevPos = pos;
+                        global.raiseEvent('scrollstart', {
+                            scrollPosition: pos
+                        }, iapi.chartInstance);
+                    });
+
+                    R.eve.on('raphael.scroll.end.' + vScrollBar.node.id, function (pos) {
+                        global.raiseEvent('scrollend', {
+                            prevScrollPosition: prevPos,
+                            scrollPosition: pos
+                        }, iapi.chartInstance);
+                    });
+                }());
+            }
+            else {
+                vScrollBar && vScrollBar.node && vScrollBar.node.hide();
+            }
+
+            if (totalVisiblelProcessWidth < totalProcessWidth) {
+                hProcessScrollBar.draw (canvasLeft - totalVisiblelProcessWidth,
+                    canvasTop + canvasHeight,{
+                        width: totalVisiblelProcessWidth,
+                        scrollRatio: totalVisiblelProcessWidth/totalProcessWidth,
+                        roundEdges: canvasConfig.isRoundEdges,
+                        fullCanvasWidth: fullCanvasWidth,
+                        windowedCanvasWidth: windowedCanvasWidth,
+                        parentLayer: scrollBarParentGroup
+                    });
+
+                // attach the callback for raising event only for it is a new scroll node.
+                !hProcessScrollNode && (function () {
+                    var prevPos;
+                    R.eve.on('raphael.scroll.start.' + hProcessScrollBar.node.id, function (pos) {
+                        prevPos = pos;
+                        global.raiseEvent('scrollstart', {
+                            scrollPosition: pos
+                        }, iapi.chartInstance);
+                    });
+
+                    R.eve.on('raphael.scroll.end.' + hProcessScrollBar.node.id, function (pos) {
+                        global.raiseEvent('scrollend', {
+                            prevScrollPosition: prevPos,
+                            scrollPosition: pos
+                        }, iapi.chartInstance);
+                    });
+                }());
+            }
+            else {
+                hProcessScrollBar && hProcessScrollBar.node && hProcessScrollBar.node.hide();
+            }
+        },
+        _createLayers: function () {
+            var iapi = this,
+                components = iapi.components,
+                paper = components.paper,
+                config = iapi.config,
+                inCanStyle = config.style.inCanvasStyle,
+                labelStyle,
+                datasetGroup,
+                graphics;
+
+            chartapi.mscartesian._createLayers.call(iapi);
+            graphics = iapi.graphics;
+            datasetGroup = graphics.datasetGroup;
+
+            graphics.parentTaskGroup = graphics.parentTaskGroup || paper.group('parentTaskGroup', datasetGroup);
+            graphics.parentConnectorsGroup = graphics.parentConnectorsGroup ||
+                paper.group('parentConnectorsGroup', datasetGroup);
+            graphics.parentMilestoneGroup = graphics.parentMilestoneGroup ||
+                paper.group('parentMilestoneGroup', datasetGroup);
+
+            labelStyle = iapi.config.milestoneLabelStyle = {
+                fontSize: pluckNumber (config.milestonefontsize,
+                    inCanStyle.fontSize) + PX,
+                fontFamily: pluck (config.milestonefont,
+                    inCanStyle.fontFamily),
+                fontWeight: pluckNumber (config.milestonefontbold, 0) && BOLD || NORMAL,
+                fontStyle: pluckNumber (config.milestonefontitalic, 0) && ITALIC || NORMAL
+            };
+
+            setLineHeight(labelStyle);
+
+            graphics.parentMilestoneGroup.css(labelStyle);
+        },
+        _drawDataset: function () {
+            this._setClipping();
+            chartapi.mscartesian._drawDataset.call(this);
+        },
+        drawLegend: function () {
+            var iapi = this,
+                config = iapi.config,
+                components = iapi.components,
+                legend = components.legend,
+                legendConf = legend.config,
+                legendWidth = legendConf.width,
+                legendHeight = legendConf.height,
+                legendPosition = legendConf.legendPos,
+                marginLeft = config.marginLeft,
+                marginTop = config.marginTop,
+                chartWidth = config.width,
+                marginRight = config.marginRight,
+                marginBottom = config.marginBottom,
+                totalWidth = config.totalWidth + config.canvasWidth,
+                totalHeight = config.totalHeight + config.canvasHeight,
+                chartHeight = config.height,
+                spaceLeft,
+                xPos,
+                yPos;
+
+            if (legendPosition === 'right') {
+                spaceLeft = totalHeight - legendHeight;
+                xPos = chartWidth - legendWidth - marginRight;
+                yPos = marginTop + (spaceLeft < 0 ? 0 : spaceLeft) / 2;
+            }
+
+            else {
+                spaceLeft = totalWidth - legendWidth;
+                xPos = marginLeft + (spaceLeft < 0 ? 0 : spaceLeft) / 2;
+                yPos = chartHeight - legendHeight - marginBottom;
+
+            }
+
+            legend.drawLegend(xPos, yPos);
+        },
+        _setClipping: function () {
+            var chart = this,
+                config = chart.config,
+                datasetLayer = chart.graphics.datasetGroup,
+                dataLabelsLayer = chart.graphics.datalabelsGroup,
+                trackerLayer  = chart.graphics.trackerGroup,
+                viewPortConfig = config.viewPortConfig,
+                scaleX = viewPortConfig.scaleX,
+                x = viewPortConfig.x,
+                animationObj = chart.config.animationObj,
+                dummyAnimElem = animationObj.dummyObj,
+                dummyAnimObj = animationObj.animObj,
+                animType = animationObj.animType,
+                animationDuration = animationObj.transposeAnimDuration,
+                clipCanvas = chart.components.canvas.config.clip['clip-canvas'].slice(0);
+
+            if (!chart.config.clipSet) {
+                datasetLayer.attr({
+                    'clip-rect': clipCanvas
+                });
+                dataLabelsLayer.attr({
+                    'clip-rect': clipCanvas
+                });
+                trackerLayer.attr({
+                    'clip-rect': clipCanvas
+                });
+            }
+            else {
+                datasetLayer.animateWith(dummyAnimElem, dummyAnimObj, {
+                    'clip-rect': clipCanvas
+                }, animationDuration, animType);
+                dataLabelsLayer.animateWith(dummyAnimElem, dummyAnimObj, {
+                    'clip-rect': clipCanvas
+                }, animationDuration, animType);
+                trackerLayer.animateWith(dummyAnimElem, dummyAnimObj, {
+                    'clip-rect': clipCanvas
+                }, animationDuration, animType);
+            }
+
+            config.xOffset = (x * scaleX);
+            datasetLayer.attr({
+                transform: 'T' + -(x * scaleX) + ',0'
+            });
+            dataLabelsLayer.attr({
+                transform: 'T' + -(x * scaleX) + ',0'
+            });
+            trackerLayer.attr({
+                transform: 'T' + -(x * scaleX) + ',0'
+            });
+            chart.config.clipSet = true;
+        },
+        _createToolBox: function () {
+            var iapi = this,
+                components = iapi.components,
+                yAxis = components.yAxis[0],
+                _scrollBar = iapi._scrollBar,
+                chartMenuBar = components.chartMenuBar || {},
+                actionBar = components.actionBar,
+                getScrollItems = _scrollBar.get,
+                addScrollItems = _scrollBar.add,
+                tb,
+                Scroller,
+                ComponentGroup,
+                hScrollItem,
+                hScrollItem1,
+                vScrollItem,
+                toolBoxAPI;
+
+            if (chartMenuBar.drawn || actionBar && actionBar.drawn) {
+                return;
+            }
+
+            // create the export or print buttons if required.
+            chartapi.mscartesian._createToolBox.call(iapi);
+            tb = components.tb;
+            toolBoxAPI = components.toolBoxAPI;
+            Scroller = toolBoxAPI.Scroller;
+            ComponentGroup = toolBoxAPI.ComponentGroup;
+            // temp code: On update scroll items needs to be reused.
+            _scrollBar.clear();
+            // Adding the scroll items in the scroll bar.
+            addScrollItems({
+                isHorizontal: true
+            }, {
+                // Attach the callback for scroll Interaction.
+                'scroll' : (function (ref, isHorizontal) {
+                    return function () {
+                        ref.updateManager.call(ref, arguments[0], isHorizontal);
+                    };
+                })(iapi, true)
+            });
+
+            addScrollItems({
+                isHorizontal: false
+            }, {
+                // Attach the callback for scroll Interaction.
+                'scroll' : (function (ref, isHorizontal) {
+
+                    return  function () {
+                        ref.updateManager.call(ref, arguments[0], isHorizontal);
+                    };
+
+                })(iapi, false)
+            });
+
+            addScrollItems({
+                isHorizontal: true
+            }, {
+                // Attach the callback for scroll Interaction.
+                'scroll' : (function () {
+                    return function () {
+                        yAxis.manageProcessScroll(arguments[0]);
+                    };
+                })(iapi, true)
+            });
+
+            // Fetching the scroll Items.
+            hScrollItem = getScrollItems()[0];
+
+            vScrollItem = getScrollItems()[1];
+
+            hScrollItem1 = getScrollItems()[2];
+
+            // adding the scrollbar. tb.pid is the component pool id
+            components.hScrollBar = components.hScrollBar || (new Scroller(hScrollItem.conf, 1, tb.pId)
+            .attachEventHandlers(hScrollItem.handler));
+
+            components.vScrollBar = components.vScrollBar || (new Scroller(vScrollItem.conf, 2, tb.pId)
+            .attachEventHandlers(vScrollItem.handler));
+
+            components.hProcessScrollBar = components.hProcessScrollBar || (
+                new Scroller(hScrollItem1.conf, 3, tb.pId).attachEventHandlers(hScrollItem1.handler));
+
+        },
+        _preDraw : function () {
+            var iapi = this;
+            iapi._setAxisValuePadding();
+            iapi._setCategories();
+            iapi.chartMenuTools.reset(iapi.components.tb, iapi);
+        },
+
+        defaultPaletteOptions : merge (extend2 ( { }, lib.defaultGaugePaletteOptions), {
+            //Colors for gauge '339900', 'DD9B02', '943A0A',
+            paletteColors:
+                [['AFD8F8', 'F6BD0F', '8BBA00', 'FF8E46', '008E8E',
+                'D64646', '8E468E', '588526', 'B3AA00', '008ED6', '9D080D',
+                'A186BE', 'CC6600', 'FDC689', 'ABA000', 'F26D7D', 'FFF200',
+                '0054A6', 'F7941C', 'CC3300', '006600', '663300', '6DCFF6'],
+                    ['AFD8F8', 'F6BD0F', '8BBA00', 'FF8E46', '008E8E',
+                'D64646', '8E468E', '588526', 'B3AA00', '008ED6', '9D080D',
+                'A186BE', 'CC6600', 'FDC689', 'ABA000', 'F26D7D', 'FFF200',
+                '0054A6', 'F7941C', 'CC3300', '006600', '663300', '6DCFF6'],
+                    ['AFD8F8', 'F6BD0F', '8BBA00', 'FF8E46', '008E8E',
+                'D64646', '8E468E', '588526', 'B3AA00', '008ED6', '9D080D',
+                'A186BE', 'CC6600', 'FDC689', 'ABA000', 'F26D7D', 'FFF200',
+                '0054A6', 'F7941C', 'CC3300', '006600', '663300', '6DCFF6'],
+                    ['AFD8F8', 'F6BD0F', '8BBA00', 'FF8E46', '008E8E',
+                'D64646', '8E468E', '588526', 'B3AA00', '008ED6', '9D080D',
+                'A186BE', 'CC6600', 'FDC689', 'ABA000', 'F26D7D', 'FFF200',
+                '0054A6', 'F7941C', 'CC3300', '006600', '663300', '6DCFF6'],
+                    ['AFD8F8', 'F6BD0F', '8BBA00', 'FF8E46', '008E8E',
+                'D64646', '8E468E', '588526', 'B3AA00', '008ED6', '9D080D',
+                'A186BE', 'CC6600', 'FDC689', 'ABA000', 'F26D7D', 'FFF200',
+                '0054A6', 'F7941C', 'CC3300', '006600', '663300', '6DCFF6']],
+            //Store other colors
+            // ------------- For 2D Chart ---------------//
+            //We're storing 5 combinations, as we've 5 defined palettes.
+            bgColor: ['FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF'],
+            bgAngle: [270, 270, 270, 270, 270],
+            bgRatio: ['100', '100', '100', '100', '100'],
+            bgAlpha: ['100', '100', '100', '100', '100'],
+            canvasBgColor: ['FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF'],
+            canvasBgAngle: [0, 0, 0, 0, 0],
+            canvasBgAlpha: ['100', '100', '100', '100', '100'],
+            canvasBgRatio: ['', '', '', '', ''],
+            canvasBorderColor: ['545454', '545454', '415D6F', '845001', '68001B'],
+            canvasBorderAlpha: [100, 100, 100, 90, 100],
+            gridColor: ['DDDDDD', 'D8DCC5', '99C4CD', 'DEC49C', 'FEC1D0'],
+            gridResizeBarColor: ['999999', '545454', '415D6F', '845001', 'D55979'],
+            categoryBgColor: ['F1F1F1', 'EEF0E6', 'F2F8F9', 'F7F0E6', 'FFF4F8'],
+            dataTableBgColor: ['F1F1F1', 'EEF0E6', 'F2F8F9', 'F7F0E6', 'FFF4F8'],
+            toolTipBgColor: ['FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF'],
+            toolTipBorderColor: ['545454', '545454', '415D6F', '845001', '68001B'],
+            baseFontColor: ['555555', '60634E', '025B6A', 'A15E01', '68001B'],
+            borderColor: ['767575', '545454', '415D6F', '845001', '68001B'],
+            borderAlpha: [50, 50, 50, 50, 50],
+            legendBgColor: ['ffffff', 'ffffff', 'ffffff', 'ffffff', 'ffffff'],
+            legendBorderColor: ['666666', '545454', '415D6F', '845001', 'D55979'],
+            plotBorderColor: ['999999', '8A8A8A', '6BA9B6', 'C1934D', 'FC819F'],
+            plotFillColor: ['EEEEEE', 'D8DCC5', 'BCD8DE', 'E9D8BE', 'FEDAE3'],
+            scrollBarColor: ['EEEEEE', 'D8DCC5', '99C4CD', 'DEC49C', 'FEC1D0']
+        })
+    }, chartapi.scrollbase, { // @todo: parse this attributes in chart level
+        taskbarroundradius: 0,
+        taskbarfillmix: ' { light-10 }, { dark-20 }, { light-50 }, { light-85 }',
+        taskbarfillratio: '0,8,84,8',
+        showslackasfill: 1,
+        dateintooltip: 1,
+        tasklabelsalign: 'center',
+        datepadding: 3,
+        showtasknames: 0,
+        showpercentlabel: false,
+        showhovereffect: 1,
+        slackfillcolor: 'FF5E5E',
+        connectorextension: 10,
+        tasklabelspadding: 2,
+        taskdatepadding: 3,
+        showlabels: UNDEFINED,
+        showtooltip: 1,
+        showtaskhovereffect: undefined,
+        useverticalscrolling: 1,
+        ganttpanedurationunit: undefined,
+        ganttpaneduration: undefined,
+        showtaskstartdate: undefined,
+        showtaskenddate: undefined,
+        ganttwidthpercent: undefined,
+        showshadow: 1,
+        enablemousetracking: false
+    });
+
+    // set attribute definations
+    setAttribDefs && setAttribDefs({
+        showpercentlabel: {
+            type: attrTypeBool,
+            pAttr: 'showpercentlabel'
+        },
+        fontsize: {
+            type: attrTypeNum
+        },
+        alpha: {
+            type: attrTypeNum
+        },
+        showborder: {
+            type: attrTypeBool
+        },
+        borderthickness: {
+            type: attrTypeNum
+        },
+        borderalpha: {
+            type: attrTypeNum
+        },
+        showHoverEffect: {
+            type: attrTypeNum
+        },
+        hoverFillAlpha: {
+            type: attrTypeNum
+        },
+        slackHoverFillColor: {
+            type: attrTypeNum
+        },
+        slackHoverFillAlpha: {
+            type: attrTypeBool
+        },
+        showlabels: {
+            type: attrTypeBool,
+            pAttr: 'showtasknames'
+        },
+        slackfillcolor: {
+            pAttr: 'slackfillcolor'
+        },
+        showtasklabels: {
+            type: attrTypeBool,
+            pAttr: 'showtasknames'
+        },
+        showtasknames: {
+            type: attrTypeBool,
+            pAttr: 'showlabels'
+        },
+        showconnectorhovereffect: {
+            type: attrTypeNum,
+            pAttr: 'showhovereffect'
+        },
+        connectorextension: {
+            type: attrTypeNum
+        },
+        tasklabelspadding: {
+            type: attrTypeNum
+        },
+        taskdatepadding: {
+            type: attrTypeNum
+        },
+        showstartdate: {
+            type: attrTypeNum,
+            pAttr: 'showtaskstartdate'
+        },
+        showenddate: {
+            type: attrTypeNum,
+            pAttr: 'showtaskenddate'
+        },
+        showtaskhovereffect: {
+            type: attrTypeNum,
+            pAttr: 'showhovereffect'
+        },
+        useverticalscrolling: {
+            type: attrTypeNum
+        },
+        taskbarroundradius: {
+            type: attrTypeNum
+        },
+        showshadow: {
+            type: attrTypeNum
+        },
+        showslackasfill: {
+            type: attrTypeNum
+        }
+    });
+
+
+    FusionCharts.register('component', ['dataset', 'Task',{
+        type: 'task',
+        configure: function(){
+            var dataSet = this,
+                jsonData = dataSet.JSONData,
+                chart = dataSet.chart,
+                userConfig = extend2({}, jsonData);
+            // set the default configurations
+            dataSet.__setDefaultConfig();
+            parseConfiguration(userConfig, dataSet.config, dataSet.chart && dataSet.chart.config, {task: true});
+            dataSet.yAxis = chart.components.yAxis[0];
+            dataSet._setConfigure();
+        },
+        _setConfigure: function (newDataset) {
+            var dataset = this,
+                datasetConfig = dataset.config,
+                chart = dataset.chart,
+                jsonData = chart.jsonData,
+                chartAttrs = jsonData.chart,
+                JSONData = dataset.JSONData,
+                setDataArr = newDataset || JSONData.task,
+                processes = jsonData.processes || {},
+                process = processes.process || [],
+                setDataLen = setDataArr && setDataArr.length,
+                colorM = chart.components.colorManager,
+                chartConfig = chart.get('config'),
+                numberFormatter = chart.get('components', 'numberFormatter'),
+                // taskRadius = chartConfig.taskbarroundradius,
+                taskMix = chartConfig.taskbarfillmix,
+                taskRatio = chartConfig.taskbarfillratio,
+                showSlackAsFill = chartConfig.showslackasfill,
+                dataStore = dataset.components.data,
+                percentComplete,
+                eDate,
+                sDate,
+                i,
+                FCDPID = '__FCDPID__',
+                arrColor,
+                arrAlpha,
+                arrRatio,
+                dataLabel,
+                serializeProcessId,
+                dInt = chartConfig.dateintooltip,
+                dataObj,
+                taskObj,
+                proLen = process.length,
+                alignVal = {
+                    right: 'right',
+                    left: 'left',
+                    'undefined': 'center',
+                    'center': 'center'
+                },
+                id,
+                tAlpha,
+                tBorderAlpha,
+                tBorderColor,
+                label,
+                color,
+                fillAngle,
+                showPercentLabel,
+                tasksMap = chart.components.tasksMap = {},
+                inCanStyle = chartConfig.style.inCanvasStyle,
+                toolText,
+                slHovColor,
+                slColor,
+                tColorObj,
+                tHovColor,
+                tHovBorderColor,
+                taskId,
+                numTasks = 0,
+                dataConfig,
+                dataLabelStyle = chartConfig.dataLabelStyle,
+                datasetLabelStyle,
+                setLabelStyle;
+            if (!dataStore) {
+                dataStore = dataset.components.data = [];
+            }
+            datasetConfig.showlabels = pluck(JSONData.showlabels, JSONData.showlabels, JSONData.showname,
+                chartAttrs.showtasklabels, chartAttrs.showtasknames, 0);
+
+            datasetLabelStyle = datasetConfig.labelStyle = extractLabelStyle({
+                fontSize: datasetConfig.fontSize,
+                fontFamily: datasetConfig.font
+            });
+
+            setLineHeight(datasetConfig.labelStyle);
+
+            // Tasks
+            if (setDataLen) {
+                // dmin = Infinity;
+                // dmax = -Infinity;
+                for (i = 0; i < setDataLen; i += 1) {
+                    taskObj = setDataArr[i];
+                    serializeProcessId = numTasks % proLen;
+                    id = pluck (taskObj.processid);
+                    if (id && typeof id === 'string') {
+                        id = id.toLowerCase();
+                    }
+                    taskId = taskObj.id;
+                    percentComplete = taskObj.percentcomplete;
+                    tAlpha = pluckNumber (taskObj.alpha, datasetConfig.alpha);
+                    color = pluck (taskObj.color, datasetConfig.color, colorM.getColor ('plotFillColor'));
+                    tBorderAlpha = pluckNumber (taskObj.borderalpha, datasetConfig.borderalpha, '100');
+                    tBorderColor = pluck (taskObj.bordercolor, datasetConfig.bordercolor,
+                        colorM.getColor('plotBorderColor'));
+
+                    label = getFirstValue (pluck (taskObj.label, taskObj.name), BLANK);
+                    //Parse the task color, ratio & alpha
+                    arrColor = colorM.parseColorMix (color, taskMix);
+                    arrAlpha = colorM.parseAlphaList (tAlpha.toString (), arrColor.length);
+                    arrRatio = colorM.parseRatioList (taskRatio, arrColor.length);
+                    fillAngle = pluckNumber (taskObj.angle, datasetConfig.angle);
+
+                    dataObj = dataStore[i] || (dataStore[i] = {
+                        config: {}
+                    });
+
+                    dataConfig = dataObj.config;
+                    dataConfig.index = i;
+                    dataConfig.link = taskObj.link;
+                    dataConfig.processId = pluck(taskObj.processid, FCDPID + serializeProcessId);
+                    // dataConfig.style = {
+                    //     fontSize: pluckNumber (taskObj.fontsize, datasetConfig.fontsize,
+                    //         inCanStyle.fontSize) + PX,
+                    //     fontFamily: pluck (taskObj.font, datasetConfig.font,
+                    //         inCanStyle.fontFamily)
+                    // };
+
+                    dataConfig.textColor = getFirstColor (pluck (taskObj.fontcolor,
+                            datasetConfig.fontcolor, inCanStyle.color));
+
+                    dataConfig.style = extractLabelStyle({
+                        fontSize: taskObj.fontsize,
+                        fontFamily: taskObj.font
+                    });
+
+                    setLineHeight(dataConfig.style);
+
+                    setLabelStyle = dataConfig.style;
+
+                    dataConfig.lineHeight = pluck(setLabelStyle && setLabelStyle.lineHeight, datasetLabelStyle &&
+                        datasetLabelStyle.lineHeight, dataLabelStyle && dataLabelStyle.lineHeight);
+
+                    dataConfig.startMs = numberFormatter.getDateValue (taskObj.start).ms;
+                    dataConfig.endMs = numberFormatter.getDateValue (taskObj.end).ms;
+
+                    sDate = numberFormatter.getFormattedDate (dataConfig.startMs);
+                    eDate = numberFormatter.getFormattedDate (dataConfig.endMs);
+
+                    dataConfig.tAlpha = tAlpha;
+                    dataConfig.tBorderColor = tBorderColor;
+                    dataConfig.tBorderAlpha = tBorderAlpha;
+                    dataLabel = BLANK;
+                    dataConfig.percentComplete = percentComplete =
+                        mathMin (pluckNumber (taskObj.percentcomplete, -1), 100);
+                    dataConfig.labelAlign = alignVal[[pluck (taskObj.labelalign,
+                        chartConfig.tasklabelsalign).toLowerCase ()]];
+
+                    dataConfig.showAsGroup = pluckNumber (taskObj.showasgroup, 0);
+                    showPercentLabel = dataConfig.showPercentLabel = pluckNumber (taskObj.showpercentlabel,
+                        datasetConfig.showpercentlabel);
+
+                    if (pluckNumber (taskObj.showlabel, taskObj.showname,
+                        datasetConfig.showlabels)) {
+                        dataLabel = label;
+                    }
+                    if (showPercentLabel && percentComplete !== -1) {
+                        dataLabel += ' ' + percentComplete + '%';
+                    }
+                    dataConfig.percentComplete = percentComplete;
+                    tColorObj = {
+                        FCcolor: {
+                            color: arrColor.join (),
+                            alpha: arrAlpha,
+                            ratio: arrRatio,
+                            angle: fillAngle
+                        }
+                    };
+                    slColor = colorM.parseColorMix (pluck (taskObj.slackfillcolor,
+                        datasetConfig.slackfillcolor), taskMix);
+
+                    slColor = showSlackAsFill ? {
+                        FCcolor: {
+                            color: slColor.join (),
+                            alpha: arrAlpha,
+                            ratio: arrRatio,
+                            angle: fillAngle
+                        }
+                    } : TRACKER_FILL;
+
+                    tHovColor = {
+                        FCcolor: {
+                            color: colorM.parseColorMix (pluck (taskObj.hoverfillcolor,
+                                datasetConfig.hoverfillcolor, chartConfig.taskhoverfillcolor,
+                                getDarkColor (color, 80)), taskMix).join (),
+                            alpha: colorM.parseAlphaList (pluck (taskObj.hoverfillalpha,
+                                datasetConfig.hoverfillalpha).toString (), arrColor.length),
+                            ratio: arrRatio,
+                            angle: fillAngle
+                        }
+                    };
+
+                    tHovBorderColor = convertColor (pluck (taskObj.hoverbordercolor,
+                        datasetConfig.hoverbordercolor, getDarkColor (tBorderColor, 80)),
+                        pluck (taskObj.hoverborderalpha, datasetConfig.hoverborderalpha,
+                        tBorderAlpha));
+
+                    slHovColor = showSlackAsFill ? {
+                        FCcolor: {
+                            color: colorM.parseColorMix (getDarkColor (pluck (taskObj.slackhoverfillcolor,
+                                datasetConfig.slackhoverfillcolor, chartConfig.slackfillcolor), 80), taskMix).join (),
+                            alpha: colorM.parseAlphaList (pluck (taskObj.slackhoverfillalpha,
+                                datasetConfig.slackhoverfillalpha, '100').toString (), arrColor.length),
+                            ratio: arrRatio,
+                            angle: fillAngle
+                        }
+                    } : TRACKER_FILL;
+
+                    dataConfig.color = toRaphaelColor (tColorObj),
+                    dataConfig.slackColor = toRaphaelColor (slColor),
+                    dataConfig.hoverFillColor = toRaphaelColor (tHovColor),
+                    dataConfig.hoverBorderColor = tHovBorderColor,
+                    dataConfig.slackHoverColor = toRaphaelColor (slHovColor),
+                    dataConfig.showHoverEffect = pluckNumber (taskObj.showhovereffect,
+                        datasetConfig.showhovereffect, chartConfig.showtaskhovereffect, 1);
+                    dataConfig.taskHeight = pluck(taskObj.height, '35%');
+                    dataConfig.topPadding = pluck (taskObj.toppadding, '35%');
+                    dataConfig.showPercentLabel = showPercentLabel;
+                    dataConfig.endDate = pluckNumber (taskObj.showenddate,
+                        datasetConfig.showenddate) ? eDate : UNDEF;
+                    dataConfig._endDate = eDate;
+                    dataConfig.startDate = pluckNumber (taskObj.showstartdate,
+                        datasetConfig.showstartdate) ? sDate : UNDEF;
+                    dataConfig._startDate = sDate;
+                    dataConfig.shadow = {
+                        opacity: mathMax (tAlpha, tBorderAlpha) / 100,
+                        inverted: true
+                    };
+                    dataConfig.id = id;
+                    dataConfig.taskId = taskId;
+                    dataConfig.borderColor = convertColor (tBorderColor,
+                        tBorderAlpha),
+                    dataConfig.borderThickness = pluckNumber (taskObj.showborder,
+                        datasetConfig.showborder) ? pluckNumber (taskObj.borderthickness,
+                        datasetConfig.borderthickness) : 0;
+                    toolText = getValidValue (parseUnsafeString (pluck (
+                        taskObj.tooltext, datasetConfig.hovertext, datasetConfig.plottooltext,
+                        chartAttrs.plottooltext)));
+                    if (toolText !== undefined) {
+                        toolText = lib.parseTooltext (toolText, [3,28,29,30,31], {
+                            end: eDate,
+                            start: sDate,
+                            label: label,
+                            percentComplete: percentComplete !== -1 ?
+                                numberFormatter.percentValue (percentComplete) : BLANK,
+                            processName: ''
+                        }, datasetConfig);
+                    }
+                    else {
+                        toolText = ((label !== BLANK) ?
+                            (label + (dInt ? ', ' : BLANK)) : BLANK) +
+                            (dInt ? (sDate + ' - ' + eDate) : BLANK);
+                    }
+                    dataConfig.label = dataLabel;
+                    dataConfig.toolText = toolText;
+                    typeof taskId === 'string' && (taskId = taskId.toLowerCase());
+                    if (taskId !== UNDEFINED) {
+                        tasksMap[taskId] = dataStore[i];
+                    }
+                    numTasks += 1;
+                }
+            }
+            dataset.visible = pluckNumber(JSONData.visible, 1);
+        },
+
+        getAxisValuePadding: function(){},
+
+        draw: function() {
+            var dataSet = this,
+                i,
+                visible = dataSet.visible,
+                chart = dataSet.chart,
+                jsonData = chart.jsonData,
+                chartConfig = chart.get('config'),
+                animationObj = chart.get('config', 'animationObj'),
+                animationDuration = animationObj.duration,
+                dummyAnimElem = animationObj.dummyObj,
+                dummyAnimObj = animationObj.animObj,
+                animType = animationObj.animType,
+                chartComponents = chart.components,
+                canvasTop = chartConfig.canvasTop,
+                paper = chartComponents.paper,
+                xAxis = chartComponents.xAxis[0],
+                len,
+                yAxis = dataSet.yAxis,
+                parentContainer = chart.graphics.parentTaskGroup,
+                components = dataSet.components,
+                dataStore = components.data,
+                dataObj,
+                config,
+                removeDataArr = components.removeDataArr,
+                removeDataArrLen = removeDataArr && removeDataArr.length,
+                dataLabelsLayer = chart.graphics.datalabelsGroup,
+                dataLabelContainer = dataSet.graphics.dataLabelContainer,
+                initAnimCallBack = function () {
+                    dataLabelContainer.show();
+                },
+                graphics = dataSet.graphics,
+                container = graphics.container,
+                shadowContainer = graphics.shadowContainer,
+                pool = components.pool || [],
+                endX,
+                startMs,
+                endMs,
+                setLink,
+                toolText,
+                height,
+                id,
+                pHeight,
+                width,
+                width2,
+                xPos,
+                yPos,
+                position,
+                taskBarElement,
+                perComElem,
+                slackElem,
+                borderThickness,
+                elemType,
+                isNewElem,
+                taskBarElementAttrs,
+                beforeAnimAttrs,
+                crispBox,
+                halfH,
+                txtAlign,
+                borderFill,
+                padding,
+                eventArgs,
+                incrementId,
+                showTooltip = chartConfig.showtooltip,
+                datePadding = chartConfig.datepadding,
+                taskHeight,
+                viewPortConfig = chartConfig.viewPortConfig,
+                x = viewPortConfig.x,
+                labelTextAttr,
+                endLabelTextAttr,
+                startLabelTextAttr,
+                jobList = chart.getJobList(),
+                proLen = jsonData.processes.process && jsonData.processes.process.length,
+                scaleX = viewPortConfig.scaleX,
+                taskBarRadius = chartConfig.taskbarroundradius,
+                shadow = chartConfig.showshadow,
+                cursor,
+                processDimention,
+                conf = dataSet.config,
+                lineHeight;
+
+            /*
+             * Creating a container group for the graphic element of column plots if
+             * not present and attaching it to its parent group.
+             */
+            if (!container) {
+                container = dataSet.graphics.container = paper.group('columns', parentContainer);
+                if (!visible) {
+                    container.hide();
+                }
+                else {
+                    container.show();
+                }
+            }
+
+            if (!dataLabelContainer) {
+                dataLabelContainer = dataSet.graphics.dataLabelContainer = paper.group('labels', dataLabelsLayer);
+                if (!visible) {
+                    dataLabelContainer.hide();
+                }
+                else {
+                    dataLabelContainer.show();
+                }
+                dataLabelContainer.css(conf.labelStyle);
+            }
+            else {
+                dataLabelContainer.removeCSS();
+                dataLabelContainer.css(conf.labelStyle);
+            }
+
+            /*
+             * Creating the shadow element container group for each plots if not present
+             * and attaching it its parent group.
+             */
+            if (!shadowContainer) {
+                // Always sending the shadow group to the back of the plots group.
+                shadowContainer = dataSet.graphics.shadowContainer =
+                    paper.group('shadow', parentContainer).toBack();
+
+                if (!visible) {
+                    shadowContainer.hide();
+                }
+            }
+
+            if (!showTooltip) {
+                container.trackTooltip(false);
+            }
+            else {
+                container.trackTooltip(true);
+            }
+
+            len = dataStore.length;
+            incrementId = 0;
+            // Create plot elements.
+            for (i = 0; i < len; i++) {
+                dataObj = dataStore[i];
+                config = dataObj && dataObj.config;
+                startMs = config && config.startMs;
+                endMs = config && config.endMs;
+                // Condition arises when user has removed data in real time update
+                if (dataObj === undefined || startMs === undefined || endMs === null) {
+                    continue;
+                }
+                toolText = config.toolText;
+                taskHeight = config.taskHeight;
+                setLink = config.link;
+                borderThickness = config.borderThickness;
+                id = config.id;
+                borderFill = config.color;
+                lineHeight = config.lineHeight;
+
+                isNewElem = false;
+                if (incrementId > proLen - 1) {
+                    incrementId = 0;
+                }
+                if (config.id !== undefined) {
+                    processDimention = yAxis.getProcessPositionById(id);
+
+                } else {
+                    processDimention = yAxis.getProcessPositionByIndex(incrementId);
+                }
+
+                incrementId++;
+                pHeight = processDimention.height;
+                padding = (pHeight * (isPercent (config.topPadding) &&
+                    toFloat (config.topPadding, 10) * 0.01)) ||
+                    pluckNumber (config.topPadding, pHeight);
+                height = config.height = (pHeight * (isPercent(taskHeight) &&
+                    toFloat(taskHeight, 10) * 0.01)) ||
+                    pluckNumber(taskHeight, pHeight);
+
+                position = config.id !== undefined ? yAxis.getProcessPositionById(id) : i;
+                xPos = config.xPos = xAxis.getAxisPosition(config.startMs) + (x * scaleX);
+                endX = xAxis.getAxisPosition(config.endMs) + (x * scaleX);
+                width = config.width = mathAbs (width2 = endX - xPos);
+                yPos = processDimention.bottom + canvasTop - pHeight;
+
+                yPos = config.yPos = (yPos + mathMin (padding, pHeight - height));
+                halfH = height * 0.5;
+                !dataObj.graphics && (dataObj.graphics = {});
+                crispBox = R.crispBound (xPos, yPos, width, height, borderThickness);
+                xPos = crispBox.x;
+                yPos = crispBox.y;
+                width = crispBox.width;
+                height = crispBox.height;
+
+                if (checkInvalidValue(xPos, yPos, width, height) === false) {
+                    continue;
+                }
+                taskBarElement = dataObj.graphics.element;
+                if (config.showAsGroup) {
+                    elemType = 'path';
+                    beforeAnimAttrs = {
+                        path: [M, xPos, yPos]
+                    };
+                    taskBarElementAttrs = {
+                        path: [M, xPos, yPos, v, height,
+                            L, xPos + halfH, yPos + halfH, H, xPos + width - halfH,
+                            L, xPos + width, yPos + height, v, -height, H, xPos]
+                    };
+                }
+                else {
+                    elemType = 'rect';
+                    taskBarElementAttrs = {
+                        x: crispBox.x,
+                        y: crispBox.y,
+                        width: crispBox.width || 1,
+                        height: height
+                    };
+                    if (animationDuration) {
+                        beforeAnimAttrs = {
+                            x: crispBox.x,
+                            y: crispBox.y,
+                            width: 0,
+                            height: height
+                        };
+                    }
+                    else {
+                        beforeAnimAttrs = taskBarElementAttrs;
+                    }
+                }
+
+                if (taskBarElement && (taskBarElement && taskBarElement.type !== elemType)) {
+                    taskBarElement.hide();
+                    taskBarElement = dataObj.graphics.element = null;
+                }
+                if (!taskBarElement) {
+                    elemType = config.showAsGroup ? 'path' : 'rect';
+                    if (pool.element && pool.element.length && (taskBarElement = pool.element[0]).type ===
+                        elemType) {
+                        dataObj.graphics.element = taskBarElement = pool.element.shift();
+                    }
+                    else {
+                        taskBarElement =  dataObj.graphics.element = paper[elemType](container)
+                            .attr(beforeAnimAttrs)
+                            .data('dataset', dataSet);
+
+                        isNewElem = true;
+                        dataSet.slackElemHandlers(taskBarElement, chart);
+                    }
+                }
+
+                eventArgs = config.eventArgs = {
+                    processId: config.processId,
+                    taskId: config.taskId,
+                    start: config._startDate,
+                    end: config._endDate,
+                    showAsGroup: config.showAsGroup,
+                    link: config.link,
+                    sourceType: 'task',
+                    percentComplete: config.percentComplete !== -1
+                };
+
+                if (config.percentComplete !== -1 && !config.showAsGroup) {
+                    width2 = width * config.percentComplete * 0.01;
+                    borderFill = TRACKER_FILL;
+                    perComElem = dataObj.graphics.taskFill;
+                    slackElem = dataObj.graphics.slackElem;
+
+                    // Percent complete element
+                    if (!perComElem) {
+                        if (pool.taskFill && pool.taskFill.length) {
+                            perComElem = dataObj.graphics.taskFill = pool.taskFill.shift();
+                        }
+                        else {
+                            perComElem = dataObj.graphics.taskFill = paper.rect(container);
+                            dataSet.slackElemHandlers(perComElem, chart);
+                            isNewElem = true;
+                            perComElem.attr({
+                                x: xPos,
+                                y: yPos,
+                                height: height,
+                                width: animationDuration ? 0 : width2
+                            });
+                        }
+                    }
+
+                    // Slack Element
+                    if (!slackElem) {
+                        // Reuse elemnt from pool if any
+                        if (pool.slackElem && pool.slackElem.length) {
+                            slackElem = dataObj.graphics.slackElem = pool.slackElem.shift();
+                        }
+                        else {
+                            slackElem = dataObj.graphics.slackElem = paper.rect(container);
+                            dataSet.slackElemHandlers(slackElem, chart);
+                            isNewElem = true;
+                            slackElem.attr({
+                                x: xPos,
+                                y: yPos,
+                                width: 0,
+                                height: height
+                            });
+                        }
+                    }
+
+                    perComElem.show().animateWith(dummyAnimElem, dummyAnimObj, {
+                        x: xPos,
+                        y: yPos,
+                        height: height,
+                        width: width2,
+                        r: 0
+                    }, animationDuration, animType);
+                    perComElem.attr({
+                        fill: config.color,
+                        cursor: setLink ? 'pointer' : BLANK,
+                        ishot: true,
+                        'stroke-width': 0
+                    })
+                    .data('chart', chart)
+                    .data('dataObj', dataObj)
+                    .data('dataset', dataSet)
+                    .tooltip (toolText);
+
+                    slackElem.show().animateWith(dummyAnimElem, dummyAnimObj, {
+                        x: (xPos + width2) || 1,
+                        y: yPos,
+                        width: width - width2,
+                        height: height,
+                        r: 0
+                    }, animationDuration, animType);
+
+                    slackElem.attr({
+                        fill: config.slackColor,
+                        cursor: setLink ? 'pointer' : BLANK,
+                        ishot: true,
+                        'stroke-width': 0
+                    });
+
+                    perComElem && perComElem.data ('eventArgs', eventArgs);
+
+                    slackElem && slackElem.data ('eventArgs', eventArgs)
+                        .data('dataObj', dataObj)
+                        .data('dataset', dataSet)
+                        .data('chart', chart);
+                }
+                else {
+                    // If there are slack elements then hide those
+                    dataObj.graphics.taskFill && dataObj.graphics.taskFill.hide();
+                    dataObj.graphics.slackElem && dataObj.graphics.slackElem.hide();
+                }
+                // set the attr
+                taskBarElement.show().animateWith(dummyAnimElem, dummyAnimObj, taskBarElementAttrs, animationDuration,
+                    animType)
+                .attr ({
+                    fill: borderFill,
+                    stroke: config.borderColor,
+                    cursor: setLink ? 'pointer' : BLANK,
+                    ishot: true,
+                    r: taskBarRadius,
+                    'stroke-width': borderThickness
+                })
+                .shadow({
+                    opacity: shadow
+                }, shadowContainer)
+                .tooltip (toolText)
+                .data('dataObj', dataObj)
+                .data('chart', chart)
+                .data('dataset', dataSet)
+                .data('eventArgs', eventArgs);
+
+                if (animationDuration && isNewElem) {
+                    dataLabelContainer.hide();
+                    taskBarElement.animateWith (dummyAnimElem, dummyAnimObj, {
+                            width: crispBox.width || 1
+                        }, animationDuration, animType, initAnimCallBack);
+
+                }
+
+                cursor = setLink ? 'pointer' : BLANK;
+                txtAlign = config.labelAlign;
+                labelTextAttr = config._labelTextAttr || (config._labelTextAttr = {});
+
+                labelTextAttr.x = xPos + (width * xAlign[txtAlign]) + alignGutter[txtAlign];
+                labelTextAttr.y = (yPos - (pInt (lineHeight, 10) * 0.5) - chartConfig.tasklabelspadding);
+                labelTextAttr.text = config.label;
+                labelTextAttr.direction = chartConfig.textDirection;
+                labelTextAttr['text-anchor'] = align[txtAlign];
+                labelTextAttr.cursor = cursor;
+                labelTextAttr.ishot = true;
+                labelTextAttr.fill = config.textColor;
+                labelTextAttr['line-height'] = lineHeight;
+
+                startLabelTextAttr = config._startLabelTextAttr || (config._startLabelTextAttr = {});
+
+                startLabelTextAttr.x = xPos - 2 - datePadding,
+                startLabelTextAttr.y = (yPos + (height * 0.5));
+
+                startLabelTextAttr.text = config.startDate;
+                startLabelTextAttr['text-anchor'] = 'end';
+                startLabelTextAttr.cursor = cursor;
+                startLabelTextAttr.ishot = true;
+                startLabelTextAttr.direction = chartConfig.textDirection;
+                startLabelTextAttr.fill = config.textColor;
+                startLabelTextAttr['line-height'] = lineHeight;
+
+                endLabelTextAttr = config._endLabelTextAttr || (config._endLabelTextAttr = {});
+
+                endLabelTextAttr.x = xPos  + width + 2 + datePadding;
+                endLabelTextAttr.y = (yPos + (height * 0.5));
+                endLabelTextAttr.text = config.endDate;
+                endLabelTextAttr.cursor = cursor;
+                endLabelTextAttr.ishot = true;
+                endLabelTextAttr.direction = chartConfig.textDirection;
+                endLabelTextAttr['text-anchor'] = 'start';
+                endLabelTextAttr.fill = config.textColor;
+                endLabelTextAttr['line-height'] = lineHeight;
+
+                config.cursor = cursor;
+                isNewElem = false;
+            }
+
+            dataSet.drawn ? dataSet.drawLabel() : jobList.labelDrawID.push(schedular.addJob(dataSet.drawLabel,
+                dataSet, [], lib.priorityList.label));
+            // Setting the drawn flag true to draw differently incase of real time draw.
+            dataSet.drawn = true;
+            for (i = 0; i < removeDataArrLen; i++) {
+                dataSet._removeDataVisuals(removeDataArr.shift());
+            }
+        },
+        drawLabel: function () {
+            var dataset = this,
+                chart = dataset.chart,
+                paper = chart.components.paper,
+                dataStore = dataset.components.data,
+                pool = dataset.components.pool || {},
+                valElem,
+                startValElem,
+                endValElem,
+                config,
+                animationObj = chart.get('config','animationObj'),
+                dummyAnimElem = animationObj.dummyObj,
+                dummyAnimObj = animationObj.animObj,
+                animationDuration = animationObj.duration,
+                animType = animationObj.animType,
+                dataObj,
+                eventArgs,
+                textAttr,
+                startLabelTextAttr,
+                endLabelTextAttr,
+                cursor,
+                dataLabelContainer = dataset.graphics.dataLabelContainer,
+                i,
+                len = dataStore.length;
+
+            for (i = 0; i < len; i++) {
+                dataObj = dataStore[i];
+                config = dataObj.config;
+                graphics = dataObj.graphics;
+                valElem = graphics.label;
+                startValElem = graphics.startValElem;
+                endValElem = graphics.endValElem;
+                cursor = config.cursor;
+                eventArgs = config.eventArgs;
+                if (defined (config.label) && config.label !== BLANK) {
+
+                    textAttr = config._labelTextAttr;
+
+                    valElem = graphics.label || (graphics.label = pool.valElem && pool.valElem.shift());
+                    if (!valElem) {
+                        valElem = dataObj.graphics.label = paper.text(textAttr, config.style, dataLabelContainer)
+                            .data('dataset', dataset);
+                        dataset.slackElemHandlers(valElem, chart);
+                    }
+                    else {
+                        valElem.removeCSS();
+                        valElem.show().animateWith(dummyAnimElem, dummyAnimObj, textAttr,
+                            animationDuration, animType)
+                        .css(config.style);
+                    }
+
+                    valElem.data('dataObj', dataObj)
+                        .data('dataObj', dataObj)
+                        .data('eventArgs', eventArgs);
+                }
+                else {
+                    valElem && valElem.hide();
+                }
+
+                textAttr = config._startLabelTextAttr;
+
+                // Task start date element
+                startValElem = dataObj.graphics.startLabel;
+
+                if (defined (config.startDate) && config.startDate !== BLANK) {
+
+                    startLabelTextAttr = config._startLabelTextAttr;
+
+                    startValElem = graphics.startLabel || (graphics.startLabel = pool.startValElem &&
+                        pool.startValElem.shift());
+
+                    if (!startValElem) {
+                        startValElem = dataObj.graphics.startLabel = paper.text(startLabelTextAttr, config.style,
+                            dataLabelContainer)
+                            .data('dataset', dataset);
+                        dataset.slackElemHandlers(startValElem, chart);
+                    }
+                    else {
+                        startValElem.removeCSS();
+                        startValElem.show().animateWith(dummyAnimElem, dummyAnimObj, startLabelTextAttr,
+                            animationDuration, animType).css(config.style);
+                    }
+
+                    startValElem.data('dataObj', dataObj)
+                        .data('chart', chart)
+                        .data('eventArgs', eventArgs);
+                }
+                else {
+                    startValElem && startValElem.hide();
+                }
+
+                // Task end date label drawing
+                endValElem = dataObj.graphics.endLabel;
+
+                if (defined (config.endDate) && config.endDate !== BLANK) {
+
+                    endLabelTextAttr = config._endLabelTextAttr;
+
+                    endValElem = graphics.endLabel || (graphics.endLabel = pool.endValElem && pool.endValElem.shift());
+
+                    if (!endValElem) {
+                        endValElem = dataObj.graphics.endLabel = paper.text(endLabelTextAttr, config.style,
+                            dataLabelContainer).data('dataset', dataset);
+                        dataset.slackElemHandlers(endValElem, chart);
+                    }
+                    else {
+                        endValElem.removeCSS();
+                        endValElem.show().animateWith(dummyAnimElem, dummyAnimObj, endLabelTextAttr,
+                            animationDuration, animType).css (config.style);
+                    }
+
+                    endValElem.data('dataObj', dataObj)
+                    .data('chart', chart)
+                    .data('eventArgs', eventArgs);
+                }
+                else {
+                    endValElem && endValElem.hide();
+                }
+            }
+
+        },
+        slackElemHandlers: function (ele, chart) {
+            var dataset = this;
+            ele && ele.click (function (e) {
+                var ele = this;
+                plotEventHandler.call (ele, chart, e);
+            })
+            .hover (function (data) {
+                var ele = this,
+                    dataObj = ele.data ('dataObj');
+                plotEventHandler.call (ele, chart, data, ROLLOVER);
+                dataObj.config.showHoverEffect && dataset.taskHoverHandler.call (ele, chart);
+            }, function (data) {
+                var ele = this,
+                    dataObj = ele.data ('dataObj');
+                plotEventHandler.call (ele, chart, data, ROLLOUT);
+                dataObj.config.showHoverEffect && dataset.taskHoverOutHandler.call (ele, chart);
+            });
+        },
+        taskHoverHandler: function () {
+            var ele = this,
+                dataObj = ele.data ('dataObj') || {},
+                dataset = ele.data('dataset'),
+                dataStore = dataset.components.data,
+                config = dataObj.config || {},
+                index = config.index,
+                graphics = dataStore[index] && dataStore[index].graphics,
+                attrib = {
+                    fill: config.hoverFillColor,
+                    stroke: config.hoverBorderColor
+                };
+
+            if (config.percentComplete !== -1 &&
+                    !config.showAsGroup) {
+                graphics.slackElem.attr ( {
+                    fill: config.slackHoverColor
+                });
+                graphics.taskFill.attr ( {
+                    fill: config.hoverFillColor
+                });
+                delete attrib.fill;
+            }
+            graphics.element.attr (attrib);
+        },
+        taskHoverOutHandler: function () {
+            var ele = this,
+                dataObj = ele.data ('dataObj') || {},
+                dataset = ele.data ('dataset') || {},
+                dataStore = dataset.components.data,
+                config = dataObj.config || {},
+                index = config.index,
+                graphics = dataStore[index] && dataStore[index].graphics,
+                attrib = {
+                    fill: config.color,
+                    stroke: config.borderColor,
+                    'stroke-width': config.borderThickness,
+                    'stroke-dasharray': config.dashedStyle
+                };
+
+            if (graphics) {
+                if (config.percentComplete !== -1 &&
+                    !config.showAsGroup) {
+                    graphics.slackElem.attr ( {
+                        fill: config.slackColor
+                    });
+                    graphics.taskFill.attr ( {
+                        fill: config.color
+                    });
+                    delete attrib.fill;
+                }
+                graphics.element.attr (attrib);
+            }
+
+        },
+        slackElemClickHandler: function (e) {
+            var ele = this,
+                chart = ele.data('chart');
+            plotEventHandler.call (ele, chart, e);
+        },
+        // Function to remove a data from a dataset during real time update.
+        _removeDataVisuals : function (dataObj) {
+            var dataSet = this,
+                pool = dataSet.components.pool || (dataSet.components.pool = {}),
+                elementPool,
+                ele,
+                graphics,
+                graphicsObj;
+            if (!dataObj) {
+                return;
+            }
+            graphics = dataObj.graphics;
+            for (ele in graphics) {
+                elementPool = pool[ele] || (pool[ele] = []);
+                graphicsObj = graphics[ele];
+                if (graphicsObj.hide && typeof graphicsObj.hide === 'function') {
+                    graphicsObj.attr({
+                        'text-bound': []
+                    });
+                    graphicsObj.hide();
+                    graphicsObj.transform && graphicsObj.transform('');
+                }
+                elementPool.push(graphics[ele]);
+            }
+        }
+    }, 'column', {
+
+        // configurations with defult values
+        showpercentlabel: undefined,
+        showlabels: undefined,
+        showborder: 1,
+        borderthickness: 1,
+        // inherated from parent
+        font: '',
+        fontcolor: '',
+        fontsize: '',
+        color: '',
+        alpha: '100',
+        angle: 270,
+        slackfillcolor: undefined,
+        borderalpha: '100',
+        hoverfillcolor: '',
+        hoverfillalpha: '100',
+        slackhoverfillalpha: '100',
+        showstartdate: UNDEFINED,
+        showenddate: UNDEFINED
+    }]);
+
+    FusionCharts.register('component', ['dataset', 'Milestone',{
+        type: 'Milestone',
+        configure: function(){
+            var dataSet = this,
+                config = dataSet.config,
+                jsonData = dataSet.JSONData,
+                chart = dataSet.chart,
+                userConfig = extend2({}, jsonData);
+
+            dataSet.__setDefaultConfig();
+            parseConfiguration(userConfig, config, {milestones: true});
+            dataSet.yAxis = chart.components.yAxis[0];
+            dataSet._setConfigure();
+        },
+        _setConfigure: function (newDataset) {
+            var dataset = this,
+                chart = dataset.chart,
+                JSONData = dataset.JSONData,
+                setDataArr = newDataset || JSONData.milestone,
+                setDataLen = setDataArr && setDataArr.length,
+                colorM = chart.components.colorManager,
+                chartConfig = chart.get('config'),
+                numberFormatter = chart.get('components', 'numberFormatter'),
+                dataStore = dataset.components.data,
+                style = chart.config.style,
+                inCanStyle = style.inCanvasStyle,
+                tasksMap = chart.components.tasksMap,
+                dataObj,
+                taskConfigObj,
+                dataConfig,
+                i,
+                setData,
+                sides,
+                color,
+                shape,
+                depth,
+                toolText,
+                hoverFillColor,
+                taskId,
+                labelStyle = chart.config.milestoneLabelStyle,
+                dateMs,
+                milestoneDate;
+
+            if (!dataStore) {
+                dataStore = dataset.components.data = [];
+            }
+            for (i = 0; i < setDataLen; i += 1) {
+                setData = setDataArr[i];
+                dataObj = dataStore[i];
+                if (!dataObj) {
+                    dataObj = dataStore[i] = {
+                        config: {}
+                    };
+                }
+                dataConfig = dataObj.config;
+                taskId = getFirstValue (setData.taskid, BLANK).toLowerCase ();
+                shape = pluck (setData.shape, 'polygon').toLowerCase ();
+
+                sides = pluckNumber (setData.numsides, 5);
+                //Restrict
+                //shape = (shape == 'star') ? shape : mapSymbolName (sides);
+                depth = 0;
+                if (shape === 'star') {
+                    depth = 0.4;
+                } else {
+                    shape = mapSymbolName (sides);
+                    shape = mapSymbolName (sides).split ('-')[0];
+                }
+
+                color = pluck (setData.color, colorM.getColor ('legendBorderColor'));
+
+                toolText = getValidValue (parseUnsafeString (pluck (setData.tooltext,
+                    setData.hovertext, chartConfig.milestonetooltext)));
+
+                dateMs = numberFormatter.getDateValue(setData.date).ms;
+                milestoneDate = numberFormatter.getFormattedDate(dateMs);
+
+                if (toolText !== undefined && tasksMap[taskId]) {
+                    taskConfigObj = tasksMap[taskId].config;
+                    toolText = lib.parseTooltext (toolText, [28,32,33,34,35,36], {
+                        date: milestoneDate,
+                        taskStartDate: taskConfigObj._startDate,
+                        taskEndDate: taskConfigObj._endDate,
+                        taskLabel: taskConfigObj.label,
+                        taskPercentComplete: taskConfigObj.percentComplete !== -1 ?
+                            numberFormatter.percentValue (taskConfigObj.percentComplete) : BLANK,
+                        // @todo add the process name from data table once created
+                        processName: ''
+                    }, setData);
+                }
+                else {
+                    toolText = milestoneDate;
+                }
+
+                // style = {
+                //     fontSize: pluckNumber (setData.fontsize, chartConfig.milestonefontsize,
+                //         inCanStyle.fontSize) + PX,
+                //     fontFamily: pluck (setData.font, chartConfig.milestonefont,
+                //         inCanStyle.fontFamily),
+                //     fontWeight: pluckNumber (setData.fontbold,
+                //         chartConfig.milestonefontbold, 0) && BOLD || NORMAL,
+                //     fontStyle: pluckNumber (setData.fontitalic,
+                //         chartConfig.milestonefontitalic, 0) && ITALIC || NORMAL
+                // };
+
+                style = dataConfig.style = extractLabelStyle({
+                    fontSize: setData.fontsize,
+                    fontFamily: setData.font,
+                    fontWeight: setData.fontbold,
+                    fontStyle: setData.fontitalic
+                });
+
+                dataConfig.textColor = getFirstColor (pluck (setData.fontcolor,
+                    chartConfig.milestonefontcolor, inCanStyle.color));
+
+
+                setLineHeight (style);
+
+                dataConfig.lineHeight = pluck(style && style.lineHeight, labelStyle && labelStyle.lineHeight);
+
+                dataConfig.numSides = sides,
+                dataConfig.startAngle = pluckNumber (setData.startangle, 90),
+                dataConfig.radius = setData.radius,
+                dataConfig.origDate = setData.date,
+                dataConfig.date = numberFormatter.getDateValue (setData.date),
+                dataConfig.fillColor = getFirstColor (color),
+                dataConfig.fillAlpha = pluckNumber (setData.fillalpha, setData.alpha, 100) * 0.01,
+                dataConfig.borderColor = getFirstColor (pluck (setData.bordercolor, color)),
+                dataConfig.borderAlpha = pluckNumber (setData.borderalpha, setData.alpha, 100) * 0.01,
+                dataConfig.displayValue = parseUnsafeString (setData.label),
+                dataConfig.style = style;
+                hoverFillColor = dataConfig.hoverFillColor = getFirstColor (pluck (setData.hoverfillcolor,
+                    chartConfig.milestonehoverfillcolor, getDarkColor (color, 80))),
+                dataConfig.hoverFillAlpha = pluckNumber (setData.hoverfillalpha,
+                    chartConfig.milestonehoverfillalpha, setData.fillalpha,
+                    setData.alpha, 100) * 0.01,
+                dataConfig.hoverBorderColor = getFirstColor (pluck (setData.hoverbordercolor,
+                    chartConfig.milestonehoverbordercolor,
+                    getDarkColor (pluck (setData.bordercolor, color), 80))),
+                dataConfig.hoverBorderAlpha = pluckNumber (setData.hoverborderalpha,
+                    chartConfig.milestonehoverborderalpha, setData.borderalpha,
+                    setData.alpha, 100) * 0.01,
+                dataConfig.showHoverEffect = pluckNumber (setData.showhovereffect,
+                    chartConfig.showmilestonehovereffect, chartConfig.showhovereffect, 1),
+
+                dataConfig.depth = depth,
+                dataConfig.taskId = taskId,
+                dataConfig.borderThickness = pluckNumber (setData.borderthickness, 1),
+                dataConfig.link = setData.link;
+                dataConfig.toolText = toolText;
+            }
+            dataset.visible = pluckNumber(JSONData.visible, 1);
+        },
+        draw: function () {
+            var dataset = this,
+                chart = dataset.chart,
+                chartComponents = chart.components,
+                components = dataset.components,
+                xAxis = chartComponents.xAxis[0],
+                dataStore = components.data,
+                taskMap = chartComponents.tasksMap,
+                chartConfig = chart.config,
+                animationObj = chart.get('config', 'animationObj'),
+                animationDuration = animationObj.duration,
+                container = dataset.graphics.container,
+                parentContainer = chart.graphics.parentMilestoneGroup,
+                dataLabelContainer = dataset.graphics.dataLabelContainer,
+                visible = dataset.visible,
+                paper = chartComponents.paper,
+                removeDataArr = dataset.components.removeDataArr || [],
+                removeDataArrLen = removeDataArr.length,
+                dummyAnimElem = animationObj.dummyObj,
+                dummyAnimObj = animationObj.animObj,
+                animType = animationObj.animType,
+                dataObj,
+                taskObj,
+                graphics,
+                config,
+                eventArgs,
+                taskConfigObj,
+                setElement,
+                xPos,
+                yPos,
+                polypath,
+                radius,
+                milestoneAnimate,
+                labelElement,
+                i,
+                ln,
+                labelAttrs,
+                jobList = chart.getJobList(),
+                showTooltip = chartConfig.showtooltip,
+                pool = dataset.components.pool || [];
+            if (!container) {
+                container = dataset.graphics.container = paper.group('milestone', parentContainer);
+                if (!visible) {
+                    container.hide();
+                }
+                else {
+                    container.show();
+                }
+            }
+            if (!dataLabelContainer) {
+                dataLabelContainer = dataset.graphics.dataLabelContainer = paper.group('labels', parentContainer);
+                if (!visible) {
+                    dataLabelContainer.hide();
+                }
+                else {
+                    dataLabelContainer.show();
+                }
+            }
+
+            if (!showTooltip) {
+                container.trackTooltip(false);
+            }
+            else {
+                container.trackTooltip(true);
+            }
+
+            ln = dataStore && dataStore.length;
+            for (i = 0; i < ln; i += 1) {
+                dataObj = dataStore[i];
+                if (!dataObj) {
+                    continue;
+                }
+                config = dataObj.config;
+                taskObj = taskMap[config.taskId];
+                !dataObj.graphics && (dataObj.graphics = {});
+                graphics = dataObj.graphics;
+                setElement = graphics.element;
+                labelElement = graphics.label;
+                if (taskObj) {
+                    taskConfigObj = taskObj.config;
+                    milestoneAnimate = R.animation ({
+                        'fill-opacity': config.fillAlpha,
+                        'stroke-opacity': config.borderAlpha
+                    },
+                    animationDuration, animType);
+
+                    eventArgs = config.eventArgs = {
+                        sides: config.sides,
+                        date: config.origDate,
+                        radius: config.radius,
+                        taskId: config.taskId,
+                        toolText: config.toolText,
+                        link: config.link,
+                        numSides: config.numSides
+                    };
+                    xPos = xAxis.getPixel (config.date.ms);
+                    yPos = taskConfigObj.yPos + (taskConfigObj.height * 0.5);
+                    radius = pluckNumber (config.radius, taskConfigObj.height * 0.6);
+
+                    if (checkInvalidValue(xPos, yPos, radius) === false) {
+                        continue;
+                    }
+                    polypath = [config.numSides, xPos, yPos, radius, config.startAngle, config.depth];
+                    if (!setElement) {
+                        if (pool.element && pool.element.length) {
+                            setElement = graphics.element = pool.element.shift();
+                        }
+                        else {
+                            setElement = graphics.element = paper.polypath(container)
+                                .click(dataset.clickHandler(chart))
+                                .hover (dataset.rollOverHandler(chart), dataset.rollOutHandler(chart));
+                        }
+                    }
+                    setElement.show().animateWith(dummyAnimElem, dummyAnimObj, {
+                        polypath: polypath
+                    }, animationDuration, animType).attr({
+                        fill: config.fillColor,
+                        'fill-opacity': animationDuration ? 0 : config.fillAlpha,
+                        stroke: config.borderColor,
+                        'stroke-opacity': animationDuration ? 0 : config.borderAlpha,
+                        groupId: 'gId' + i,
+                        ishot: true,
+                        cursor: config.link ? 'pointer' : BLANK,
+                        'stroke-width': config.borderThickness
+                    })
+                    .tooltip(config.toolText)
+                    .data ('eventArgs', eventArgs)
+                    .data ('dataObj', dataObj);
+
+                    if (animationDuration && milestoneAnimate && !dataset.drawn) {
+                        graphics.element.animateWith(dummyAnimElem, dummyAnimObj,
+                            milestoneAnimate.delay (animationDuration));
+                    }
+
+                    labelAttrs = config._labelAttrs || (config._labelAttrs = {});
+                    labelAttrs.x = xPos;
+                    labelAttrs.y = yPos;
+                    labelAttrs.text = config.displayValue;
+                    labelAttrs.groupId  = 'gId' + i;
+                    labelAttrs.cursor = config.link ? 'pointer' : BLANK;
+                    labelAttrs.ishot = true;
+                    labelAttrs.direction = chartConfig.textDirection;
+                    labelAttrs['text-anchor']  = 'middle';
+                    labelAttrs.fill = config.textColor;
+                }
+                else {
+                    setElement && setElement.hide();
+                    labelElement && labelElement.hide();
+                }
+            }
+
+            dataset.drawn ? dataset.drawLabel() : jobList.labelDrawID.push(schedular.addJob(dataset.drawLabel,
+                dataset, [], lib.priorityList.label));
+
+            dataset.drawn = true;
+            for (i = 0; i < removeDataArrLen; i++) {
+                dataset._removeDataVisuals(removeDataArr.shift());
+            }
+        },
+        drawLabel: function () {
+            var dataset = this,
+                chart = dataset.chart,
+                paper = chart.components.paper,
+                animationObj = chart.get('config', 'animationObj'),
+                animationDuration = animationObj.duration,
+                animType = animationObj.animType,
+                dummyAnimElem = animationObj.dummyObj,
+                dummyAnimObj = animationObj.animObj,
+                tasksMap = chart.components.tasksMap,
+                config,
+                dataLabelContainer = dataset.graphics.dataLabelContainer,
+                eventArgs,
+                dataObj,
+                i,
+                labelElement,
+                len,
+                graphics,
+                labelAttrs,
+                taskObj,
+                pool = dataset.components.pool || {},
+                dataStore = dataset.components.data,
+                style;
+
+            for (i = 0, len = dataStore.length; i < len; i++) {
+                dataObj = dataStore[i];
+                config = dataObj.config;
+                taskObj = tasksMap[config.taskId];
+                graphics = dataObj.graphics;
+                labelElement = graphics.label;
+                eventArgs = config.eventArgs;
+                labelAttrs = config._labelAttrs;
+                style = config.style;
+                if (config.displayValue !== BLANK && config.displayValue !== UNDEFINED && taskObj) {
+                    labelElement = graphics.label || (graphics.label = pool.label && pool.label.shift());
+                    // Drawing of milestone label element
+                    if (!labelElement) {
+                        labelElement = graphics.label = paper.text (labelAttrs, style, dataLabelContainer)
+                            .click (dataset.clickHandler(chart))
+                            .hover (dataset.rollOverHandler(chart), dataset.rollOutHandler(chart));
+                    }
+                    else {
+                        labelElement.removeCSS();
+                        labelElement.show().animateWith(dummyAnimElem, dummyAnimObj, labelAttrs,
+                            animationDuration, animType)
+                        .css (style);
+                    }
+
+                    labelElement.tooltip(config.toolText)
+                        .data ('eventArgs', eventArgs)
+                        .data ('dataObj', dataObj);
+                }
+                else {
+                    labelElement && labelElement.hide();
+                }
+            }
+
+        },
+        clickHandler: function (chart) {
+            return  function (event) {
+                var ele = this;
+                /**
+                 * In `Gantt` chart, milestones are an important part of the chart as they allow you to visually
+                 * depict any crucial dates on the chart.
+                 * This event is fired when a milestone is clicked
+                 *
+                 * This event is only applicable to Gantt chart.
+                 *
+                 * @event FusionCharts#milestoneClick
+                 *
+                 * @param {string} date - The date of the milestone.
+                 * @param {string} numSides - The number of sides of the milestone.
+                 * @param {string} radius - The radius of the milestone.
+                 * @param {string} taskId - The id of the task to which this milestone relates to.
+                 * @param {string} toolText - The tooltext that is displayed when hovered over the milestone
+                 * @param {number} chartX - x-coordinate of the pointer relative to the chart.
+                 * @param {number} chartY - y-coordinate of the pointer relative to the chart.
+                 * @param {number} pageX - x-coordinate of the pointer relative to the page.
+                 * @param {number} pageY - y-coordinate of the pointer relative to the page.
+                 * @see FusionCharts#event:milestoneRollOver
+                 * @see FusionCharts#event:milestoneRollOut
+                 */
+                plotEventHandler.call(ele, chart, event, 'MilestoneClick');
+            };
+
+        },
+        rollOverHandler: function (chart) {
+            return function (event) {
+                var ele = this,
+                    dataObj = ele.data('dataObj'),
+                    config = dataObj.config;
+
+                /**
+                 * In `Gantt` chart, milestones are an important part of the chart as they allow you to visually
+                 * depict any crucial dates on the chart.
+                 * This event is fired when the pointer moves over a milestone
+                 *
+                 * This event is only applicable to Gantt chart.
+                 *
+                 * @event FusionCharts#milestoneRollOver
+                 *
+                 * @param {string} date - The date of the milestone.
+                 * @param {string} numSides - The number of sides of the milestone.
+                 * @param {string} radius - The radius of the milestone.
+                 * @param {string} taskId - The id of the task to which this milestone relates to.
+                 * @param {string} toolText - The tooltext that is displayed when hovered over the milestone
+                 * @param {number} chartX - x-coordinate of the pointer relative to the chart.
+                 * @param {number} chartY - y-coordinate of the pointer relative to the chart.
+                 * @param {number} pageX - x-coordinate of the pointer relative to the page.
+                 * @param {number} pageY - y-coordinate of the pointer relative to the page.
+                 * @see FusionCharts#event:milestoneClick
+                 * @see FusionCharts#event:milestoneRollOut
+                 */
+                plotEventHandler.call(ele, chart, event, 'MilestoneRollOver');
+
+                config.showHoverEffect && dataObj.graphics.element.attr({
+                    fill: config.hoverFillColor,
+                    stroke: config.hoverBorderColor,
+                    'fill-opacity': config.hoverFillAlpha,
+                    'stroke-opacity': config.hoverBorderAlpha
+                });
+            };
+
+        },
+        rollOutHandler: function (chart) {
+            return function (event) {
+                var ele = this,
+                    dataObj = ele.data('dataObj'),
+                    config = dataObj.config;
+
+                /**
+                 * In `Gantt` chart, milestones are an important part of the chart as they allow you to visually
+                 * depict any crucial dates on the chart.
+                 * This event is fired when the pointer moves out of a milestone
+                 *
+                 * This event is only applicable to Gantt chart.
+                 *
+                 * @event FusionCharts#milestoneRollOut
+                 *
+                 * @param {string} date - The date of the milestone.
+                 * @param {string} numSides - The number of sides of the milestone.
+                 * @param {string} radius - The radius of the milestone.
+                 * @param {string} taskId - The id of the task to which this milestone relates to.
+                 * @param {string} toolText - The tooltext that is displayed when hovered over the milestone
+                 * @param {number} chartX - x-coordinate of the pointer relative to the chart.
+                 * @param {number} chartY - y-coordinate of the pointer relative to the chart.
+                 * @param {number} pageX - x-coordinate of the pointer relative to the page.
+                 * @param {number} pageY - y-coordinate of the pointer relative to the page.
+                 * @see FusionCharts#event:milestoneClick
+                 * @see FusionCharts#event:milestoneRollOver
+                 */
+                plotEventHandler.call(ele, chart, event, 'MilestoneRollOut');
+
+                config.showHoverEffect && dataObj.graphics.element.attr({
+                    fill: config.fillColor,
+                    stroke: config.borderColor,
+                    'fill-opacity': config.fillAlpha,
+                    'stroke-opacity': config.borderAlpha
+                });
+            };
+        }
+    }, 'Task', {
+
+        // configurations with defult values
+        showpercentlabel: 0,
+        showstartdate: 0,
+        showenddate: 0,
+        showlabels: undefined,
+        showborder: 1,
+        borderthickness: 1,
+        showHoverEffect: 1,
+        slackFillColor: 'FF5E5E',
+        // inherated from parent
+        font: '',
+        fontcolor: '',
+        fontsize: '',
+        color: '',
+        alpha: '100', // not found
+        bordercolor: '',// not found
+        borderalpha: '100', // not found
+        hoverFillColor: '',// not found
+        hoverFillAlpha: '100', // not found
+        slackHoverFillColor: 10, // not found
+        slackHoverFillAlpha: '100' // notfound
+
+    }]);
+
+    FusionCharts.register('component', ['dataset', 'Connectors',{
+        configure: function () {
+            var dataSet = this,
+                config = dataSet.config,
+                jsonData = dataSet.JSONData,
+                chart = dataSet.chart,
+                userConfig = extend2({}, jsonData);
+
+            dataSet.__setDefaultConfig();
+            parseConfiguration(userConfig, config, {connector: true});
+            dataSet.yAxis = chart.components.yAxis[0];
+            dataSet._setConfigure();
+        },
+
+        _setConfigure: function (newDataset) {
+            var dataset = this,
+                datasetConfig = dataset.config,
+                chart = dataset.chart,
+                JSONData = dataset.JSONData,
+                setDataArr = newDataset || JSONData.connector,
+                setDataLen = (setDataArr && setDataArr.length) || 0,
+                colorM = chart.components.colorManager,
+                chartConfig = chart.get('config'),
+                dataStore = dataset.components.data,
+                i,
+                dataObj,
+                setData,
+                config,
+                cnColor,
+                cnAlpha,
+                cnThickness,
+                cnIsDashed;
+            if (!dataStore) {
+                dataStore = dataset.components.data = [];
+            }
+            // Connectors
+            for (i = 0; i < setDataLen; i += 1) {
+                setData = setDataArr[i];
+                dataObj = dataStore[i];
+                if (!dataObj) {
+                    dataObj = dataStore[i] = {
+                        config : {}
+                    };
+                }
+                !dataObj.config && (dataObj.config = {});
+                config = dataObj.config;
+                // Extract the attributes
+                cnColor = pluck (setData.color, datasetConfig.color, colorM.getColor ('plotBorderColor'));
+                cnAlpha = pluckNumber (setData.alpha, datasetConfig.alpha, 100);
+                cnThickness = pluckNumber (setData.thickness, datasetConfig.thickness, 1);
+                cnIsDashed = pluckNumber (setData.isdashed, datasetConfig.isdashed, 1);
+
+                config.fromTaskId = getFirstValue (setData.fromtaskid, BLANK).toLowerCase ();
+                config.toTaskId = getFirstValue (setData.totaskid, BLANK).toLowerCase (),
+                config.fromTaskConnectStart = pluckNumber (setData.fromtaskconnectstart, 0),
+                config.toTaskConnectStart = pluckNumber (setData.totaskconnectstart, 1),
+                config.color = convertColor (cnColor);
+                config.alpha = cnAlpha * 0.01;
+                config.link = setData.link;
+
+                config.showHoverEffect = pluckNumber (setData.showhovereffect,
+                    datasetConfig.showhovereffect, chartConfig.showconnectorhovereffect, 1);
+
+                config.hoverColor = convertColor (pluck (setData.hovercolor,
+                    datasetConfig.hovercolor, chartConfig.connectorhovercolor,
+                    getDarkColor (cnColor, 80)),
+                    pluckNumber (setData.hoveralpha,
+                    datasetConfig.hoveralpha, chartConfig.connectorhoveralpha, cnAlpha));
+
+                config.hoverThickness = pluckNumber (setData.hoverthickness,
+                    datasetConfig.hoverthickness, chartConfig.connectorhoverthickness,
+                        cnThickness);
+
+                config.thickness = cnThickness;
+
+                config.dashedStyle = cnIsDashed ?
+                    getDashStyle (pluckNumber (setData.dashlen,
+                    datasetConfig.dashlen, 5),
+                    pluckNumber (setData.dashgap, datasetConfig.dashgap,
+                    cnThickness), cnThickness) : DASH_DEF;
+            }
+            dataset.visible = pluckNumber(JSONData.visible, 1);
+        },
+
+        draw: function () {
+            var dataset = this,
+                chart = dataset.chart,
+                chartComponents = chart.components,
+                dataStore = dataset.components.data,
+                paper = chartComponents.paper,
+                chartConfig = chart.config,
+                ln = dataStore.length,
+                taskMap = chartComponents.tasksMap,
+                cExt = chartConfig.connectorextension,
+                parentContainer = chart.graphics.parentConnectorsGroup,
+                container = dataset.graphics.container,
+                pool = dataset.components.pool || [],
+                visible = dataset.visible,
+                animationObj = chart.get('config', 'animationObj'),
+                animationDuration = animationObj.duration,
+                dummyAnimElem = animationObj.dummyObj,
+                dummyAnimObj = animationObj.animObj,
+                animType = animationObj.animType,
+                removeDataArr = dataset.components.removeDataArr || [],
+                removeDataArrLen = removeDataArr.length,
+                startTaskId,
+                endTaskId,
+                stTObj,
+                endTObj,
+                dataObj,
+                graphics,
+                isStraightLine,
+                stY,
+                etY,
+                stx1,
+                stx2,
+                etx1,
+                etx2,
+                diff,
+                cnCase,
+                config,
+                stTConfig,
+                endTConfig,
+                opacity,
+                cPath,
+                tH,
+                connector,
+                initialAnimDuration,
+                eventArgs,
+                applyFn,
+                trackerElement,
+                dashLength,
+                dashGap,
+                connectorAnimation,
+                i;
+            if (!container) {
+                container = dataset.graphics.container = paper.group('connectors', parentContainer);
+                if (!visible) {
+                    container.hide();
+                }
+                else {
+                    container.show();
+                }
+            }
+            //Iterate through each and draw it
+            for (i = 0; i <= ln; i += 1) {
+                dataObj = dataStore[i];
+                if (!dataObj) {
+                    continue;
+                }
+                config = dataObj.config;
+                !dataObj.graphics && (dataObj.graphics = {});
+                graphics = dataObj.graphics;
+                startTaskId = config.fromTaskId && config.fromTaskId.toLowerCase();
+                endTaskId = config.toTaskId && config.toTaskId.toLowerCase();
+                stTObj = taskMap[startTaskId];
+                endTObj = taskMap[endTaskId];
+                connector = graphics.connector;
+                //If the connector's from and to Id are defined, only then we draw the connector
+                if (stTObj && endTObj) {
+                    stTConfig = stTObj.config;
+                    endTConfig = endTObj.config;
+                    //Y Positions
+                    stY = stTConfig.yPos + (stTConfig.height * 0.5);
+                    etY = endTConfig.yPos + (endTConfig.height * 0.5);
+                    //Check if the to and from bars are in straight line
+                    isStraightLine = (stY == etY);
+                    //Dash properties
+                    dashLength = 3;
+                    dashGap = config.isDashed ? (config.thickness + 2) : 0;
+                    //X Positions
+                    stx1 = stTConfig.xPos;
+                    stx2 = stTConfig.xPos + stTConfig.width;
+                    etx1 = endTConfig.xPos;
+                    etx2 = endTConfig.xPos + endTConfig.width;
+                    if (checkInvalidValue(stx1, stx2, etx1, etx2) === false) {
+                        continue;
+                    }
+                    diff = 0;
+                    //There can be four cases if the two tasks are not in straight line
+                    cnCase = 0;
+                    //cnCase 1: End of StartTask to Start of EndTask
+                    if (config.fromTaskConnectStart === 0 && config.toTaskConnectStart === 1) {
+                        cnCase = 1;
+                    }
+                    //cnCase 2: End of StartTask to End of EndTask
+                    if (config.fromTaskConnectStart === 0 && config.toTaskConnectStart === 0) {
+                        cnCase = 2;
+                    }
+                    //cnCase 3: Start of StartTask to Start of EndTask
+                    if (config.fromTaskConnectStart === 1 && config.toTaskConnectStart === 1) {
+                        cnCase = 3;
+                    }
+                    //cnCase 4: Start of StartTask to End of EndTask
+                    if (config.fromTaskConnectStart === 1 && config.toTaskConnectStart === 0) {
+                        cnCase = 4;
+                    }
+
+
+                    if (isStraightLine) {
+                        tH = stTConfig.height;
+                        switch (cnCase) {
+                            case 1 :
+                                diff = (etx1 - stx2) / 10;
+                                cPath = [M, stx2, stY, stx2 + diff, stY,
+                                    L, stx2 + diff, stY, stx2 + diff, stY - tH,
+                                    L, stx2 + diff, stY - tH, etx1 - diff, stY - tH,
+                                    L, etx1 - diff, stY - tH, etx1 - diff, stY,
+                                    L, etx1 - diff, stY, etx1, etY];
+                                break;
+                            case 2 :
+                                cPath = [M, stx2, stY, stx2 + cExt, stY,
+                                    L, stx2 + cExt, stY, stx2 + cExt, stY - tH,
+                                    L, stx2 + cExt, stY - tH, etx2 + cExt, stY - tH,
+                                    L, etx2 + cExt, etY - tH, etx2 + cExt, etY,
+                                    etx2, etY];
+                                break;
+                            case 3 :
+                                cPath = [M, stx1, stY, stx1 - cExt, stY,
+                                    L, stx1 - cExt, stY, stx1 - cExt, stY - tH,
+                                    L, stx1 - cExt, stY - tH, etx1 - cExt, stY - tH,
+                                    L, etx1 - cExt, stY - tH, etx1 - cExt, stY,
+                                    L, etx1 - cExt, stY, etx1, stY];
+
+                                break;
+                            case 4 :
+                                cPath = [M, stx1, stY, stx1 - cExt, stY,
+                                    L, stx1 - cExt, stY, stx1 - cExt, stY - tH,
+                                    L, stx1 - cExt, stY - tH, etx2 + cExt, stY - tH,
+                                    L, etx2 + cExt, stY - tH, etx2 + cExt, stY,
+                                    L, etx2 + cExt, stY, etx2, stY];
+                                break;
+                        }
+                    } else {
+                        switch (cnCase) {
+                            case 1 :
+                                cPath = [M, stx2, stY, stx2 + (etx1 - stx2) / 2, stY,
+                                   L, stx2 + (etx1 - stx2) / 2, stY, stx2 + (etx1 - stx2) / 2, etY,
+                                   L, stx2 + (etx1 - stx2) / 2, etY, etx1, etY];
+
+                                if (stx2 <= etx1) {
+                                    cPath = [M, stx2, stY, stx2 + (etx1 - stx2) / 2, stY,
+                                        L, stx2 + (etx1 - stx2) / 2, stY, stx2 + (etx1 - stx2) / 2, etY,
+                                        L, stx2 + (etx1 - stx2) / 2, etY, etx1, etY];
+                                } else {
+                                    cPath = [M, stx2, stY, stx2 + cExt, stY,
+                                        L, stx2 + cExt, stY, stx2 + cExt, stY + (etY - stY) / 2,
+                                        L, stx2 + cExt, stY + (etY - stY) / 2, etx1 - cExt, stY + (etY - stY) / 2,
+                                        L, etx1 - cExt, stY + (etY - stY) / 2, etx1 - cExt, etY,
+                                        L, etx1 - cExt, etY, etx1, etY];
+                                }
+                                break;
+                            case 2 :
+                                diff = ((etx2 - stx2) < 0) ? (0) : (etx2 - stx2);
+                                cPath = [M, stx2, stY, stx2 + cExt + diff, stY,
+                                    L, stx2 + cExt + diff, stY, stx2 + cExt + diff, etY,
+                                    L, stx2 + cExt + diff, etY, etx2, etY];
+                                break;
+                            case 3 :
+                                diff = ((stx1 - etx1) < 0) ? (0) : (stx1 - etx1);
+                                cPath = [M, stx1, stY, stx1 - cExt - diff, stY,
+                                    L, stx1 - cExt - diff, stY, stx1 - cExt - diff, etY,
+                                    L, stx1 - cExt - diff, etY, etx1, etY];
+                                break;
+                            case 4 :
+                                if (stx1 > etx2) {
+                                    cPath = [M, stx1, stY, stx1 - (stx1 - etx2) / 2, stY,
+                                        L, stx1 - (stx1 - etx2) / 2, stY, stx1 - (stx1 - etx2) / 2, etY,
+                                        L, stx1 - (stx1 - etx2) / 2, etY, etx2, etY];
+                                } else {
+                                    cPath = [M, stx1, stY, stx1 - cExt, stY,
+                                        L, stx1 - cExt, stY, stx1 - cExt, stY + (etY - stY) / 2,
+                                        L, stx1 - cExt, stY + (etY - stY) / 2, etx2 + cExt, stY + (etY - stY) / 2,
+                                        L, etx2 + cExt, stY + (etY - stY) / 2, etx2 + cExt, etY,
+                                        L, etx2 + cExt, etY, etx2, etY];
+                                }
+                                break;
+                        }
+                    }
+                    if (!connector) {
+                        if (pool.connector && pool.connector.length) {
+                            connector = graphics.connector = pool.connector.shift();
+                        }
+                        else {
+                            connector = graphics.connector = paper.path(container);
+                        }
+                        if (animationDuration) {
+                            // clip-canvas animation to line chart
+                            connectorAnimation = R.animation ( {
+                                'stroke-opacity': config.alpha
+                            },
+                            animationDuration, animType);
+                            applyFn = ATTRFN;
+                            opacity = 0;
+                            initialAnimDuration = animationDuration;
+                        }
+                        else {
+                            applyFn = ATTRFN;
+                            opacity = config.alpha;
+                        }
+                    }
+
+                    connector.show().animateWith(dummyAnimElem, dummyAnimObj, {
+                        path: cPath
+                    }, animationDuration, animType)
+                    .attr({
+                        stroke: config.color,
+                        'stroke-opacity': opacity,
+                        'stroke-width': config.thickness,
+                        'stroke-dasharray': config.dashedStyle
+                    });
+
+                    if (connectorAnimation) {
+                        connector.animate(connectorAnimation.delay(initialAnimDuration || 0));
+                    }
+
+                    eventArgs = {
+                        fromTaskId: config.fromTaskId,
+                        toTaskId: config.toTaskId,
+                        fromTaskConnectStart: config.fromTaskConnectStart,
+                        toTaskConnectStart: config.toTaskConnectStart,
+                        link: config.link,
+                        sourceType: 'connector'
+                    };
+
+                    trackerElement = graphics.tracker;
+                    if (!trackerElement) {
+                        if (pool.tracker && pool.tracker.length) {
+                            trackerElement = graphics.tracker = pool.tracker.shift();
+                        }
+                        else {
+                            trackerElement = graphics.tracker = paper.path (container)
+                                .click (dataset.connectorClick(chart))
+                                .hover (dataset.rollOverHandler(chart), dataset.rollOutHandler(chart));
+                        }
+                    }
+                    trackerElement.attr ({
+                        path: cPath,
+                        stroke: TRACKER_FILL,
+                        'stroke-width': mathMax (config.thickness, 10),
+                        ishot: true,
+                        cursor: config.link ? 'pointer' : BLANK
+                    })
+                    .data ('dataObj', dataObj)
+                    .data ('eventArgs', eventArgs);
+                }
+                else {
+                    if (connector) {
+                        connector.hide();
+                    }
+                }
+            }
+            for (i = 0; i < removeDataArrLen; i++) {
+                dataset._removeDataVisuals(removeDataArr.shift());
+            }
+        },
+
+        connectorClick: function (chart) {
+
+            return function (e) {
+                var ele = this;
+                plotEventHandler.call(ele, chart, e, 'ConnectorClick');
+            };
+
+        },
+
+        rollOverHandler: function (chart) {
+
+            return function (event) {
+                var ele = this,
+                    data = ele.data('dataObj'),
+                    config = data.config,
+                    chartComponents = chart.components,
+                    taskMap = chartComponents.tasksMap,
+                    stTObj = taskMap[config.fromTaskId],
+                    endTObj = taskMap[config.toTaskId],
+                    attr = {
+                        stroke: config.hoverColor,
+                        'stroke-dasharray': config.dashedStyle,
+                        'stroke-width': config.hoverThickness
+                    },
+                    connector = data.graphics.connector;
+
+                plotEventHandler.call(ele, chart, event, 'ConnectorRollOver');
+                if (config.showHoverEffect) {
+                    each([stTObj, endTObj], function (obj) {
+                        var attrib = {
+                                fill: obj.config.hoverFillColor,
+                                stroke: obj.config.hoverBorderColor
+                            },
+                            percentComplete = obj.config.percentComplete,
+                            slackGraphic = obj.graphics.slackElem,
+                            element = obj.graphics.element,
+                            // Percent complete graphic is task fill
+                            percentCompleteGraphic = obj.graphics.taskFill;
+                        if (percentComplete &&
+                                !obj.config.showAsGroup) {
+                            slackGraphic && slackGraphic.attr({
+                                fill: obj.config.slackHoverColor
+                            });
+                            percentCompleteGraphic && percentCompleteGraphic.attr({
+                                fill: obj.config.hoverFillColor,
+                                stroke: obj.config.hoverBorderColor
+                            });
+                            delete attrib.fill;
+                        }
+                        element && element.attr(attrib);
+                    });
+                    connector && connector.attr(attr);
+                }
+            };
+
+        },
+
+        rollOutHandler: function (chart) {
+
+            return function (event) {
+                var ele = this,
+                    data = ele.data('dataObj'),
+                    config = data.config,
+                    chartComponents = chart.components,
+                    taskMap = chartComponents.tasksMap,
+                    stTObj = taskMap[config.fromTaskId],
+                    endTObj = taskMap[config.toTaskId],
+                    attr = {
+                        stroke: config.color,
+                        'stroke-width': config.thickness,
+                        'stroke-dasharray': config.dashedStyle
+                    },
+                    connector = data.graphics.connector;
+
+                plotEventHandler.call(ele, chart, event, 'ConnectorRollOut');
+
+                if (config.showHoverEffect) {
+                    each([stTObj, endTObj], function (obj) {
+                        var attrib = {
+                            fill: obj.config.color,
+                            stroke: obj.config.borderColor,
+                            'stroke-width': obj.config.borderThickness,
+                            'stroke-dasharray': obj.config.dashedStyle
+                        },
+                        percentComplete = obj.config.percentComplete,
+                        slackGraphic = obj.graphics.slackElem,
+                        element = obj.graphics.element,
+                        percentCompleteGraphic = obj.graphics.taskFill;
+                        if (percentComplete &&
+                                !obj.config.showAsGroup) {
+                            slackGraphic && slackGraphic.attr({
+                                fill: obj.config.slackColor
+                            });
+                            percentCompleteGraphic && percentCompleteGraphic.attr({
+                                fill: obj.config.color
+                            });
+                            delete attrib.fill;
+                        }
+                        element && element.attr(attrib);
+                    });
+                    connector && connector.attr(attr);
+                }
+            };
+
+        }
+    }, 'Task', {
+        isdashed: 1,
+        thickness: 1
+    }]);
+
+    FusionCharts.get ('component', ['axis', 'ganttCommon', {
+        _drawPlotLine : function () {
+            var axis = this,
+                axisConfig = axis.config,
+                chart = axis.chart,
+                chartComponents = chart.components,
+                canvas = axisConfig.canvas,
+                chartConfig = chart.config,
+                axisPlotLineContainerBottom = axisConfig.ganttPlotLineContainer,
+                canvasBottom = canvas.canvasBottom || chartConfig.canvasBottom,
+                canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
+                canvasRight = canvas.canvasRight || chartConfig.canvasRight,
+                canvasTop = canvas.canvasTop || chartConfig.canvasTop,
+                paper = chartComponents.paper,
+                gridArr = axisConfig.gridArr,
+                plotLine = axis.graphics.line || [],
+                animateAxis = axisConfig.animateAxis,
+                animationDuration,
+                transposeAnimDuration,
+                lineStyle,
+                lineElement,
+                counter = 0,
+                i,
+                ln,
+                path = [],
+                animObj,
+                dummyObj,
+                animType;
+
+            animationDuration = chart.get('config', 'animationObj');
+            animObj = animationDuration.animObj;
+            dummyObj = animationDuration.dummyObj;
+            transposeAnimDuration = animationDuration.transposeAnimDuration;
+            animType = animationDuration.animType;
+            lineStyle = {
+                'stroke-dasharray': axisConfig.plotLineDashStyle,
+                'stroke-width': axisConfig.plotLineThickness,
+                stroke: axisConfig.plotLineColor
+            };
+            for (i = 0, ln = gridArr.length; i < ln; i += 1) {
+                if (gridArr[i].x !== undefined) {
+                    path.push('M',gridArr[i].x,canvasTop,'L', gridArr[i].x, canvasBottom);
+                } else {
+                    path.push('M',canvasLeft,gridArr[i].y,'L', canvasRight, gridArr[i].y);
+                }
+            }
+            if (plotLine[counter]) {
+                lineElement = plotLine[counter].graphics;
+                if (transposeAnimDuration && animateAxis) {
+                    lineElement.animateWith(dummyObj, animObj, {path : path}, transposeAnimDuration, animType);
+                } else {
+                    lineElement.attr({path : path});
+                }
+                lineElement.attr(lineStyle);
+            } else {
+                lineElement = paper.path(path, axisPlotLineContainerBottom);
+                lineElement.attr(lineStyle);
+                plotLine[counter] = {};
+                plotLine[counter].graphics = lineElement;
+            }
+            counter += 1;
+            for (i = counter, ln = plotLine.length; i < ln; i += 1) {
+                plotLine[i].graphics.attr({
+                    path : 'M0,0'
+                });
+            }
+            axis.graphics.line = plotLine;
+        },
+        _drawPlotBand : function () {
+
+        },
+        translateAxis : function (x, y) {
+            var axis = this,
+                axisConfig = axis.config,
+                isVertical = axisConfig.isVertical,
+                lastTranslate = axisConfig.lastTranslate || (axisConfig.lastTranslate = {x : 0, y : 0}),
+                ganttPlotHoverBandContainer = axisConfig.ganttPlotHoverBandContainer,
+                dx,
+                dy;
+
+            dx = x !== undefined ? x - lastTranslate.x : 0;
+            dy = y !== undefined ? y - lastTranslate.y : 0;
+            lastTranslate.x = x !== undefined ? x : lastTranslate.x;
+            lastTranslate.y = y !== undefined ? y : lastTranslate.y;
+
+            axisConfig.labelContainer && axisConfig.labelContainer.translate(dx,dy);
+            axisConfig.hotContainer && axisConfig.hotContainer.translate(dx,dy);
+            axisConfig.headerContainer && axisConfig.headerContainer.translate(dx,0);
+            if (isVertical) {
+                axisConfig.ganttPlotLineContainer && axisConfig.ganttPlotLineContainer.translate(0,dy);
+                ganttPlotHoverBandContainer && ganttPlotHoverBandContainer.translate(0,dy);
+            } else {
+                axisConfig.ganttPlotLineContainer && axisConfig.ganttPlotLineContainer.translate(dx,0);
+                ganttPlotHoverBandContainer && ganttPlotHoverBandContainer.translate(dx,0);
+                axis.setAxisConfig({
+                    animateAxis : false
+                });
+                axisConfig.drawTrendLines && axis._drawTrendLine();
+                axis.setAxisConfig({
+                    animateAxis : true
+                });
+            }
+        },
+        resetTransletAxis : function () {
+            var axis = this,
+                axisConfig = axis.config,
+                transformAttr;
+
+            transformAttr = {
+                transform : 't0,0'
+            };
+            axisConfig.lastTranslate = {x : 0, y : 0};
+            axisConfig.labelContainer && axisConfig.labelContainer.attr(transformAttr);
+            axisConfig.headerContainer && axisConfig.headerContainer.attr(transformAttr);
+            axisConfig.ganttPlotLineContainer && axisConfig.ganttPlotLineContainer.attr(transformAttr);
+            axisConfig.ganttPlotHoverBandContainer && axisConfig.ganttPlotHoverBandContainer.attr(transformAttr);
+            axisConfig.hotContainer && axisConfig.hotContainer.attr(transformAttr);
+        },
+        _drawProcessAndDataTableStyleParser : function (attrs) {
+            var axis = this,
+                axisConfig = axis.config,
+                chart = axis.chart,
+                smartLabel = chart.linkedItems.smartLabel,
+                components = chart.components,
+                colorM = components.colorManager,
+                labels = axisConfig.labels,
+                style = labels.style,
+                elem = attrs.elem || {},
+                dimension = attrs.dimension,
+                textStyle = elem._attrib || {},
+                left = dimension.left,
+                right = dimension.right,
+                top = dimension.top,
+                bottom = dimension.bottom,
+                processPadding = 2,
+                fontFamily,
+                fontSize,
+                fontWeight,
+                fontStyle,
+                bgColor,
+                xPos,
+                yPos,
+                text,
+                retAttrib,
+                smartText,
+                vAlign,
+                align,
+                color,
+                textDecoration,
+                hoverC,
+                hoverA,
+                useHover,
+                usePlotHover,
+                rollOverColor,
+                link,
+                lHeight;
+
+            switch (attrs.type) {
+                case 'category' :
+                case 'datatable' :
+                case 'process' :
+                    fontFamily = pluck(textStyle.font, style.fontFamily);
+                    fontSize = pluck(textStyle.fontsize, style.fontSize).replace(/px/i, '') + PXSTRING;
+                    fontStyle = pluck((Number(textStyle.isitalic) ? 'italic' : undefined), style.fontStyle);
+                    bgColor = convertColor(pluck (textStyle.bgcolor ? getFirstColor(textStyle.bgcolor) : undefined ,
+                        colorM.getColor ('categoryBgColor')), pluckNumber(textStyle.bgalpha, 100));
+                    color = pluck(textStyle.fontcolor ? getFirstColor(textStyle.fontcolor) : undefined, style.color),
+                    textDecoration = pluckNumber (textStyle.isunderline, 0) && UND_LINE || NONE;
+                    vAlign = (pluck(textStyle.valign, 'center')).toLowerCase();
+                    align = (pluck(textStyle.align, 'middle')).toLowerCase();
+                    text = elem.drawLabel || '';
+                    fontWeight = pluck((Number(textStyle.isbold) ? 'bold' : undefined), style.fontWeight);
+                    link = elem.link;
+                    break;
+                case 'header' :
+                    fontFamily = pluck(textStyle.headerfont, style.fontFamily);
+                    fontSize = pluck(textStyle.headerfontsize, style.fontSize).replace(/px/i, '') + PXSTRING;
+                    fontWeight = pluck((Number(textStyle.headerisbold) === 1 ? 'bold' : textStyle.headerisbold ===
+                        undefined ? 'bold' : undefined), style.fontWeight);
+                    color = pluck (textStyle.headerfontcolor ? getFirstColor(textStyle.headerfontcolor) :
+                        undefined, style.color),
+                    textDecoration = pluckNumber (textStyle.headerisunderline, 0) && UND_LINE || NONE;
+                    fontStyle = pluck((textStyle.headerisitalic ? 'italic' : undefined), style.fontStyle);
+                    bgColor = convertColor(pluck (textStyle.headerbgcolor ? getFirstColor(textStyle.headerbgcolor)
+                        : undefined , colorM.getColor ('categoryBgColor')), pluckNumber(textStyle.headerbgalpha, 100));
+                    vAlign = (pluck(textStyle.headervalign, 'center')).toLowerCase();
+                    align = (pluck(textStyle.headeralign, 'middle')).toLowerCase();
+                    text = elem.drawLabel || '';
+                    link = elem.headerlink;
+                    break;
+            }
+            switch (attrs.type) {
+                case 'category' :
+                    axisConfig.gridLinePath += 'M'+left+','+top+'L'+left+','+bottom+'L'+right+','+bottom;
+                    fontWeight = pluck((Number(textStyle.isbold) === 1 ? 'bold' : textStyle.isbold === undefined ?
+                        'bold' : undefined), style.fontWeight);
+                    break;
+                case 'datatable' :
+                case 'process' :
+                    axisConfig.gridLinePath += 'M'+left+','+bottom+'L'+right+','+bottom+'L'+right+','+top;
+                    break;
+                case 'header' :
+                    axisConfig.gridLineHeaderPath += 'M'+left+','+bottom+'L'+right+','+bottom+'L'+right+','+top;
+                    break;
+            }
+            hoverC = pluck (elem._attrib.hoverbandcolor, axisConfig.hoverColor);
+            hoverA = pluckNumber (elem._attrib.hoverbandalpha, axisConfig.hoverAlpha);
+            if (align === 'left') {
+                xPos = left + processPadding;
+                align = 'start';
+            } else if (align === 'right') {
+                xPos = left + (right - left) - processPadding;
+                align = 'end';
+            } else {
+                align = 'middle';
+                xPos = left + (right - left)/2;
+            }
+            if (vAlign === 'top') {
+                yPos = top - processPadding;
+            } else if (vAlign === 'bottom') {
+                yPos = top + (bottom - top) - processPadding;
+            } else {
+                vAlign = 'center';
+                yPos = top + (bottom - top)/2;
+            }
+            style = {
+                fontFamily: fontFamily,
+                fontSize: fontSize,
+                fontWeight: fontWeight,
+                fontStyle: fontStyle,
+                textDecoration : textDecoration
+            };
+            lHeight = setLineHeight(style);
+            lHeight = Number((lHeight.replace(/px/i, '')));
+            lHeight = bottom - top > lHeight ? bottom - top : lHeight;
+            smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
+            smartLabel.setStyle (style);
+            smartText = smartLabel.getSmartText (text, right - left, lHeight);
+            retAttrib = {
+                    textAttr : {
+                        x : xPos,
+                        y : yPos,
+                        text : smartText.text,
+                        fill: color,
+                        'text-anchor' : align,
+                        'vertical-align' : vAlign,
+                        cursor : link ? 'pointer' : 'default'
+                    },
+                    css : style,
+                    rectAttr : {
+                        x : left,
+                        y : top,
+                        width : left < right ? right - left : 0,
+                        height : top < bottom ? bottom - top : 0,
+                        fill: bgColor,
+                        'stroke-width' : 0,
+                        cursor : link ? 'pointer' : 'default'
+                    },
+                    eventArgs : {
+                        isHeader: attrs.type === 'header',
+                        label: text,
+                        vAlign: vAlign,
+                        align: align,
+                        link: link,
+                        id: elem.id
+                    },
+                    tooltext : smartText.oriText
+                };
+            if (attrs.type === 'datatable' || attrs.type === 'process' || attrs.type === 'category') {
+                rollOverColor = convertColor (hoverC, hoverA);
+                useHover = pluckNumber(elem._attrib.showhoverband, axisConfig.useHover);
+                usePlotHover = pluckNumber(elem._attrib.showganttpanehoverband, axisConfig.usePlotHover, useHover);
+                retAttrib.dataArgs = {
+                    rollOverColor: rollOverColor,
+                    useHover: useHover,
+                    usePlotHover: usePlotHover,
+                    dimension: dimension,
+                    hoverEle: elem,
+                    type : attrs.type,
+                    pos : attrs.pos,
+                    axis : axis,
+                    groupId : attrs.elemIndex
+                };
+            } else {
+                retAttrib.dataArgs = {
+                    rollOverColor: undefined,
+                    useHover: 0,
+                    usePlotHover: 0,
+                    dimension: dimension,
+                    hoverEle: elem,
+                    type : attrs.type,
+                    pos : attrs.pos,
+                    axis : axis,
+                    groupId : attrs.elemIndex
+                };
+            }
+            return retAttrib;
+        },
+        _drawProcessAndDataTableElement : function(attrs) {
+            var axis = this,
+                axisConfig = axis.config,
+                chart = axis.chart,
+                chartComponents = chart.components,
+                categoryElement = axis.components.categoryElement || [],
+                hoverElemsArr = axisConfig.hoverElemsArr || (axisConfig.hoverElemsArr = []),
+                paper = chartComponents.paper,
+                elemIndex = attrs.elemIndex,
+                labelHoverEventName = axisConfig.labelHoverEventName,
+                animateAxis = axisConfig.animateAxis,
+                animationDuration,
+                transposeAnimDuration,
+                attribs,
+                labelBackContainer,
+                labelLineContainer,
+                labelTextContainer,
+                rectElement,
+                textElement,
+                animObj,
+                dummyObj,
+                showTooltip = chart.config.showtooltip,
+                animType,
+
+                labelClickHandler = function (e) {
+                    var ele = this;
+                    /**
+                     * In `Gantt` chart, category element distributes the time line into visual divisions
+                     * This event is fired when a category is clicked.
+                     *
+                     * This event is only applicable to Gantt chart.
+                     *
+                     * @event FusionCharts#categoryClick
+                     *
+                     * @param { string } align - The alignment of the category label.
+                     * @param { string } vAlign - The vertical alignment of the category label.
+                     * @param { number } chartX - x-coordinate of the pointer relative to the chart.
+                     * @param { number } chartY - y-coordinate of the pointer relative to the chart.
+                     * @param { number } pageX - x-coordinate of the pointer relative to the page.
+                     * @param { number } pageY - y-coordinate of the pointer relative to the page.
+                     * @param { string } link - URL set for the category on mouse click.
+                     * @param { string } text - The label in the category
+                     * @see FusionCharts#event:categoryRollOver
+                     * @see FusionCharts#event:categoryRollOut
+                     */
+                    plotEventHandler.call (ele, chart, e, labelHoverEventName.click);
+                },
+                labelRollOver = function (e) {
+                    var ele = this;
+                    hoverTimeout = clearTimeout (hoverTimeout);
+                    if (!lastHoverEle || lastHoverEle.removed) {
+                        lastHoverEle = null;
+                    }
+                    lastHoverEle && axis._gridOutHandler.call (lastHoverEle);
+                    axis._gridHoverHandler.call (ele);
+                    /**
+                     * In `Gantt` chart, category element distributes the time line into visual divisions
+                     * This event is fired when the pointer moves over a category.
+                     *
+                     * This event is only applicable to Gantt chart.
+                     *
+                     * @event FusionCharts#categoryRollOver
+                     *
+                     * @param { string } align - The alignment of the category label.
+                     * @param { string } vAlign - The vertical alignment of the category label.
+                     * @param { number } chartX - x-coordinate of the pointer relative to the chart.
+                     * @param { number } chartY - y-coordinate of the pointer relative to the chart.
+                     * @param { number } pageX - x-coordinate of the pointer relative to the page.
+                     * @param { number } pageY - y-coordinate of the pointer relative to the page.
+                     * @param { string } link - URL set for the category on mouse click.
+                     * @param { string } text - The label in the category
+                     * @see FusionCharts#event:categoryClick
+                     * @see FusionCharts#event:categoryRollOut
+                     */
+
+                    plotEventHandler.call (ele, chart, e, labelHoverEventName.rollOver);
+                },
+                labelRollOut = function (e) {
+                    lastHoverEle = this;
+                    hoverTimeout = clearTimeout (hoverTimeout);
+                    hoverTimeout = setTimeout (function () {
+                        axis._gridOutHandler.call (lastHoverEle);
+                    }, 500);
+                    /**
+                     * In `Gantt` chart, category element distributes the time line into visual divisions
+                     * This event is fired when the pointer moves out of a category.
+                     *
+                     * This event is only applicable to Gantt chart.
+                     *
+                     * @event FusionCharts#categoryRollOut
+                     *
+                     * @param { string } align - The alignment of the category label.
+                     * @param { string } vAlign - The vertical alignment of the category label.
+                     * @param { number } chartX - x-coordinate of the pointer relative to the chart.
+                     * @param { number } chartY - y-coordinate of the pointer relative to the chart.
+                     * @param { number } pageX - x-coordinate of the pointer relative to the page.
+                     * @param { number } pageY - y-coordinate of the pointer relative to the page.
+                     * @param { string } link - URL set for the category on mouse click.
+                     * @param { string } text - The label in the category
+                     * @see FusionCharts#event:categoryClick
+                     * @see FusionCharts#event:categoryRollOver
+                     */
+                    plotEventHandler.call (lastHoverEle, chart, e, labelHoverEventName.rollOut);
+                };
+
+            animationDuration = chart.get('config', 'animationObj');
+            animObj = animationDuration.animObj;
+            dummyObj = animationDuration.dummyObj;
+            transposeAnimDuration = animationDuration.transposeAnimDuration;
+            animType = animationDuration.animType;
+            if (attrs.type === 'header') {
+                labelBackContainer = axisConfig.headerBackContainer;
+                labelLineContainer = axisConfig.headerLineContainer;
+                labelTextContainer = axisConfig.headerTextContainer;
+            } else {
+                labelBackContainer = axisConfig.labelBackContainer;
+                labelLineContainer = axisConfig.labelLineContainer;
+                labelTextContainer = axisConfig.labelTextContainer;
+            }
+            attribs = axis._drawProcessAndDataTableStyleParser(attrs);
+
+            if (categoryElement[elemIndex]) {
+                textElement = categoryElement[elemIndex].graphics.label;
+                rectElement = categoryElement[elemIndex].graphics.rect;
+                labelBackContainer.appendChild(rectElement);
+                labelTextContainer.appendChild(textElement);
+                if (transposeAnimDuration && animateAxis) {
+                    rectElement.animateWith(dummyObj, animObj,attribs.rectAttr, transposeAnimDuration, animType);
+                    textElement.animateWith(dummyObj, animObj,attribs.textAttr, transposeAnimDuration, animType);
+                } else {
+                    rectElement.attr(attribs.rectAttr);
+                    textElement.attr(attribs.textAttr);
+                }
+                textElement.css(attribs.css);
+            } else {
+                rectElement = paper.rect(attribs.rectAttr, labelBackContainer);
+                textElement = paper.text(attribs.textAttr, attribs.css, labelTextContainer);
+                categoryElement[elemIndex] = {};
+                categoryElement[elemIndex].graphics = {};
+                categoryElement[elemIndex].config = {};
+                categoryElement[elemIndex].graphics.label = textElement;
+                categoryElement[elemIndex].graphics.rect = rectElement;
+                rectElement.hover (labelRollOver, labelRollOut)
+                .click (labelClickHandler);
+                textElement
+                .hover (labelRollOver, labelRollOut)
+                .click (labelClickHandler);
+            }
+            if (attrs.type !== 'header') {
+                if (!hoverElemsArr[attrs.pos]) {
+                    hoverElemsArr[attrs.pos] = [];
+                }
+                hoverElemsArr[attrs.pos].push({
+                    bgElem : rectElement,
+                    bgColor : attribs.rectAttr.fill
+                });
+            }
+            rectElement
+            .data ('dataObj', attrs.elem)
+            .data ('eventArgs', attribs.eventArgs)
+            .data ('data', attribs.dataArgs);
+            textElement
+            .data ('dataObj', attrs.elem)
+            .data ('eventArgs', attribs.eventArgs)
+            .data ('data', attribs.dataArgs)
+            .tooltip(attribs.tooltext)
+            .trackTooltip(showTooltip ? true : false);
+
+            axis.components.categoryElement = categoryElement;
+        },
+        _drawGridLine : function () {
+            var axis = this,
+                axisConfig = axis.config,
+                chart = axis.chart,
+                chartComponents = chart.components,
+                paper = chartComponents.paper,
+                gridLine = axis.graphics.gridLine || [],
+                animateAxis = axisConfig.animateAxis,
+                animationDuration,
+                transposeAnimDuration,
+                lineStyle,
+                lineElement,
+                counter = 0,
+                i = 0,
+                ln = 2,
+                path,
+                animObj,
+                dummyObj,
+                container,
+                animType;
+
+            animationDuration = chart.get('config', 'animationObj');
+            animObj = animationDuration.animObj;
+            dummyObj = animationDuration.dummyObj;
+            transposeAnimDuration = animationDuration.transposeAnimDuration;
+            animType = animationDuration.animType;
+            lineStyle = {
+                'stroke-dasharray': axisConfig.lineDashStyle,
+                'stroke-width': axisConfig.lineThickness,
+                stroke: axisConfig.lineColor
+            };
+            for (; i < ln; i += 1) {
+                if (i === 0) {
+                    path = axisConfig.gridLinePath;
+                    container = axisConfig.labelLineContainer;
+                } else {
+                    path = axisConfig.gridLineHeaderPath;
+                    container = axisConfig.headerLineContainer;
+                    if (!path) {
+                        continue;
+                    }
+                }
+                if (gridLine[counter]) {
+                    lineElement = gridLine[counter].graphics;
+                    if (transposeAnimDuration && animateAxis) {
+                        lineElement.animateWith(dummyObj, animObj, {path : path}, transposeAnimDuration, animType);
+                    } else {
+                        lineElement.attr({path : path});
+                    }
+                    lineElement.attr(lineStyle);
+                } else {
+                    lineElement = paper.path(path, container);
+                    lineElement.attr(lineStyle);
+                    gridLine[counter] = {};
+                    gridLine[counter].graphics = lineElement;
+                }
+                counter += 1;
+            }
+            for (i = counter, ln = gridLine.length; i < ln; i += 1) {
+                gridLine[i].graphics.attr({
+                    path : 'M0,0'
+                });
+            }
+            axis.graphics.gridLine = gridLine;
+        },
+        _gridHoverHandler : function () {
+            var data = this.data ('data'),
+                type = data.type,
+                dimension = data.dimension,
+                axis = data.axis,
+                chart = axis.chart,
+                chartConfig = chart.config,
+                paper = chart.components.paper,
+                axisConfig = axis.config,
+                hoverElemsArr = axisConfig.hoverElemsArr || [],
+                ganttPlotHoverBandContainer = axisConfig.ganttPlotHoverBandContainer,
+                plotHoverElement = axis.graphics.plotHoverElement || (axis.graphics.plotHoverElement = []),
+                plotHoverElementAttr,
+                i,
+                ln;
+
+            if (type === 'category') {
+                plotHoverElementAttr = {
+                    x : dimension.left,
+                    y : chartConfig.canvasTop,
+                    width : dimension.left < dimension.right ? (dimension.right - dimension.left) : 0,
+                    height : chartConfig.height,
+                    fill: data.rollOverColor,
+                    'stroke-width' : 0
+                };
+            } else {
+                plotHoverElementAttr = {
+                    y : dimension.top,
+                    x : chartConfig.canvasLeft,
+                    height : dimension.top < dimension.bottom ? (dimension.bottom - dimension.top) : 0,
+                    width : chartConfig.width,
+                    fill: data.rollOverColor,
+                    'stroke-width' : 0
+                };
+            }
+            if (data.usePlotHover) {
+                if (plotHoverElement[0]) {
+                    plotHoverElement[0].attr(plotHoverElementAttr).show();
+                } else {
+                    plotHoverElement[0] = paper.rect(plotHoverElementAttr, ganttPlotHoverBandContainer);
+                }
+            }
+            if (data.useHover && hoverElemsArr[data.pos]) {
+                for (i = 0, ln = hoverElemsArr[data.pos].length; i < ln; i += 1) {
+                    hoverElemsArr[data.pos][i].bgElem.attr({
+                        fill : data.rollOverColor
+                    });
+                }
+            }
+        },
+        _gridOutHandler : function () {
+            var data = this.data ('data'),
+                axis = data.axis,
+                axisConfig = axis.config,
+                hoverElemsArr = axisConfig.hoverElemsArr || [],
+                plotHoverElement = axis.graphics.plotHoverElement || [],
+                i,
+                ln,
+                hoverOutElem;
+
+            if (data.usePlotHover) {
+                if (plotHoverElement[0]) {
+                    plotHoverElement[0].hide();
+                }
+            }
+            if (data.useHover && hoverElemsArr[data.pos]) {
+                for (i = 0, ln = hoverElemsArr[data.pos].length; i < ln; i += 1) {
+                    hoverOutElem = hoverElemsArr[data.pos][i];
+                    hoverOutElem.bgElem.attr({
+                        fill : hoverOutElem.bgColor
+                    });
+                }
+            }
+        },
+        _disposeExtraProcessAndDataTableElement : function(index) {
+            var axis = this,
+                categoryElement = axis.components.categoryElement || [],
+                i,
+                ln;
+
+            for (i = index, ln = categoryElement.length; i < ln; i += 1) {
+                categoryElement[i].graphics.label.attr({
+                    text : ''
+                });
+                categoryElement[i].graphics.rect.attr({
+                    x : 0,
+                    y : 0,
+                    width : 0,
+                    heigth : 0
+                });
+            }
+        }
+    }, 'cartesian']);
+
+    FusionCharts.get ('component', ['axis', 'time', {
+        configure : function () {
+            var axis = this,
+                axisConfig = axis.config,
+                axisAttr = axisConfig.rawAttr,
+                chart = axis.chart,
+                jsonData = chart.jsonData,
+                FCChartObj = jsonData.chart,
+                chartComponents = chart.components,
+                colorM = chartComponents.colorManager,
+                parent = FusionCharts.register('component', ['axis', 'cartesian']);
+
+            parent.prototype.configure.call(this);
+            //Gantt grid line properties
+            axisConfig.plotLineColor = axisConfig.lineColor = convertColor (pluck (FCChartObj.ganttlinecolor,
+                colorM.getColor ('gridColor')), pluckNumber (FCChartObj.ganttlinealpha, 100));
+            axisConfig.plotLineThickness = axisConfig.lineThickness = pluckNumber (FCChartObj.ganttlinethickness, 1);
+            axisConfig.plotLineDashStyle = axisConfig.lineDashStyle = pluckNumber (FCChartObj.ganttlinedashed, 0) ?
+                getDashStyle (pluckNumber (FCChartObj.ganttlinedashlen, 1),
+                FCChartObj.ganttlinedashgap, axisConfig.lineThickness) : DASH_DEF;
+            axisConfig.hoverColor = pluck (FCChartObj.categoryhoverbandcolor, FCChartObj.hoverbandcolor,
+                colorM.getColor ('gridColor'));
+            axisConfig.hoverAlpha = pluckNumber (FCChartObj.categoryhoverbandalpha, FCChartObj.hoverbandalpha, 30);
+            axisConfig.useHover = pluckNumber (FCChartObj.showcategoryhoverband, FCChartObj.showhoverband,
+                FCChartObj.showhovereffect, 1);
+            axisConfig.usePlotHover = pluckNumber (FCChartObj.showganttpaneverticalhoverband);
+            axisConfig.trendlinesDashLen = pluckNumber(axisAttr.trendlinesDashLen, 3);
+            axisConfig.trendlinesDashGap = pluckNumber(axisAttr.trendlinesDashGap, 3);
+
+            axisConfig.gridLineHeaderPath = '';
+            axisConfig.gridLinePath = '';
+        },
+        setCategory : function (categories) {
+            var axis = this,
+                chart = axis.chart,
+                components = chart.get('components'),
+                numberFormatter = components.numberFormatter,
+                axisConfig = axis.config,
+                axisRange = axisConfig.axisRange,
+                startPad = axisConfig.startPad || 0,
+                endPad = axisConfig.endPad || 0,
+                axisLimits,
+                catLength,
+                categoriesFinal,
+                catObj,
+                startMS,
+                endMS,
+                minTime = Infinity,
+                maxTime = -Infinity,
+                i,
+                j;
+
+            // Initialize the category object
+            axisConfig.categories = {};
+            // Set the category flag to true
+            if (categories) {
+                axisConfig.hasCategory = 1;
+            } else {
+                axisConfig.hasCategory = 0;
+                return;
+            }
+            // this will store the category
+            categoriesFinal = axisConfig.categories.category = extend2({}, categories);
+            axis._extractAttribToEnd(categoriesFinal, {});
+            for (i in categoriesFinal) {
+                if (!categoriesFinal.hasOwnProperty(i) || i === '_attrib') {
+                    continue;
+                }
+                for (j = 0, catLength = categoriesFinal[i].category.length; j < catLength; j += 1) {
+                    catObj = categoriesFinal[i].category[j];
+                    startMS = numberFormatter.getDateValue(catObj.start).ms;
+                    endMS = numberFormatter.getDateValue(catObj.end).ms;
+
+                    if (isNaN(startMS)) { /** @todo nan check without fn call */
+                        startMS = UNDEF;
+                    }
+                    if (startMS > maxTime) { maxTime = startMS; }
+                    if (startMS <= minTime) { minTime = startMS; }
+
+                    if (isNaN(endMS)) { /** @todo nan check without fn call */
+                        endMS = UNDEF;
+                    }
+                    if (endMS > maxTime) { maxTime = endMS; }
+                    if (endMS <= minTime) { minTime = endMS; }
+                }
+            }
+
+            axisLimits = {
+                Max : maxTime + endPad,
+                Min : minTime - startPad,
+                divGap : 1
+            };
+
+            axisRange.min = Number(toPrecision(axisLimits.Min,10));
+            axisRange.max = Number(toPrecision(axisLimits.Max, 10));
+            axisRange.tickInterval = Number(toPrecision(axisLimits.divGap, 10));
+        },
+        _drawComponents : function () {
+            var axis = this,
+                axisConfig = axis.config;
+
+            axis._drawCategories();
+            axisConfig.drawPlotlines && axis._drawPlotLine();
+            axisConfig.drawPlotBand && axis._drawPlotBand();
+            axisConfig.drawTrendLines && axis._drawTrendLine();
+        },
+        _drawCategories : function () {
+            var axis = this,
+                axisConfig = axis.config,
+                axisDimention = axisConfig.axisDimention || {},
+                axisStartPosition = axisDimention.y,
+                spaceTaken = axisConfig.totalHeight || 0,
+                chart = axis.chart,
+                chartComponents = chart.components,
+                chartConfig = chart.config,
+                viewPortConfig = chartConfig.viewPortConfig,
+                paper = chartComponents.paper,
+                numberFormatter = chartComponents.numberFormatter,
+                canvas = axisConfig.canvas,
+                gridArr = axisConfig.gridArr || (axisConfig.gridArr = []),
+                canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
+                canvasTop = canvas.canvasTop || chartConfig.canvasTop,
+                canvasHeight = canvas.canvasHeight || chartConfig.canvasHeight,
+                canvasWidth = canvas.canvasWidth || chartConfig.canvasWidth,
+                lastTranslate = axisConfig.lastTranslate || (axisConfig.lastTranslate = {x : 0, y : 0}),
+                layers = chart.graphics,
+                axisBottomGroup = layers.axisBottomGroup,
+                i,
+                categories,
+                category,
+                elemIndex = 0,
+                j,
+                args,
+                lastRightPos,
+                animationDuration,
+                animObj,
+                dummyObj,
+                transposeAnimDuration,
+                animType,
+                labelClipHeight,
+                startms,
+                endms;
+
+            animationDuration = chart.get('config', 'animationObj');
+            animObj = animationDuration.animObj;
+            dummyObj = animationDuration.dummyObj;
+            transposeAnimDuration = animationDuration.transposeAnimDuration;
+            animType = animationDuration.animType;
+            labelClipHeight = mathMin(spaceTaken, canvasTop - (axisConfig.maxTopSpaceAvailable || 0));
+            labelClipHeight = labelClipHeight > 0 ? labelClipHeight : 0;
+
+            layers.ganttPlotHoverBandContainerParent = layers.ganttPlotHoverBandContainerParent ||
+                paper.group('guntt-plot-band-container-parent', axisBottomGroup);
+
+            if (!axisConfig.ganttPlotHoverBandContainer) {
+                axisConfig.ganttPlotHoverBandContainer = paper.group('guntt-plot-band-container',
+                    layers.ganttPlotHoverBandContainerParent);
+                axisConfig.ganttPlotHoverBandContainer.attr({
+                    'clip-rect': canvasLeft +','+ canvasTop +
+                    ','+canvasWidth+ ','+canvasHeight
+                });
+            } else {
+                axisConfig.ganttPlotHoverBandContainer.animateWith(dummyObj, animObj, {
+                    'clip-rect': canvasLeft +','+ canvasTop +
+                    ','+canvasWidth+ ','+canvasHeight
+                }, transposeAnimDuration, animType);
+            }
+            if (!axisConfig.ganttPlotLineContainer) {
+                axisConfig.ganttPlotLineContainer = paper.group('guntt-plot-line-container', axisBottomGroup);
+                axisConfig.ganttPlotLineContainer.attr({
+                    'clip-rect': canvasLeft +','+ canvasTop +
+                    ','+canvasWidth+ ','+canvasHeight
+                });
+            } else {
+                axisConfig.ganttPlotLineContainer.animateWith(dummyObj, animObj, {
+                    'clip-rect': canvasLeft +','+ canvasTop +
+                    ','+canvasWidth+ ','+canvasHeight
+                }, transposeAnimDuration, animType);
+            }
+            if (!axisConfig.labelContainer) {
+                axisConfig.labelContainer = paper.group('guntt-label-container', axisBottomGroup);
+                axisConfig.labelContainer.attr({
+                    'clip-rect': canvasLeft +','+ (canvasTop - labelClipHeight) +
+                    ','+canvasWidth+ ','+labelClipHeight
+                });
+            } else {
+                axisConfig.labelContainer.animateWith(dummyObj, animObj, {
+                    'clip-rect': canvasLeft +','+ (canvasTop - labelClipHeight) +
+                    ','+canvasWidth+ ','+labelClipHeight
+                }, transposeAnimDuration, animType);
+            }
+            if (!axisConfig.labelBackContainer) {
+                axisConfig.labelBackContainer = paper.group('guntt-label-back-container', axisConfig.labelContainer);
+            }
+            if (!axisConfig.labelLineContainer) {
+                axisConfig.labelLineContainer = paper.group('guntt-label-line-container', axisConfig.labelContainer);
+            }
+            if (!axisConfig.labelTextContainer) {
+                axisConfig.labelTextContainer = paper.group('guntt-label-text-container', axisConfig.labelContainer);
+            }
+            axisConfig.gridLinePath = '';
+            axisConfig.gridLineHeaderPath = '';
+            axisConfig.hoverElemsArr = [];
+            axisConfig.labelHoverEventName = {
+                click : 'CategoryClick',
+                rollOver : 'CategoryRollOver',
+                rollOut : 'CategoryRollOut'
+            };
+
+            if (axisConfig.hasCategory) {
+                categories = axisConfig.categories.category;
+                for (i in categories) {
+                    if (!categories.hasOwnProperty(i) || i === '_attrib') {
+                        continue;
+                    }
+                    category = categories[i].category;
+                    lastRightPos = undefined;
+                    gridArr = axisConfig.gridArr = [];
+                    for (j in category) {
+                        startms = numberFormatter.getDateValue(category[j].start).ms;
+                        endms = numberFormatter.getDateValue(category[j].end).ms;
+                        if (!category.hasOwnProperty(j) || j === '_attrib' || isNaN(startms) || isNaN(endms)) {
+                            continue;
+                        }
+                        args = {
+                            elem : category[j],
+                            elemIndex : elemIndex,
+                            pos : elemIndex,
+                            dimension : {
+                                left : lastRightPos ||
+                                    axis.getPixel(startms),
+                                right : axis.getPixel(endms),
+                                top : axisStartPosition - spaceTaken + categories[i]._attrib.topPos,
+                                bottom : axisStartPosition - spaceTaken + categories[i]._attrib.bottomPos
+                            },
+                            type : 'category',
+                            isHeader : false
+                        };
+                        lastRightPos = args.dimension.right;
+                        axis._drawProcessAndDataTableElement(args);
+                        elemIndex += 1;
+                        gridArr.push({x : args.dimension.left});
+                    }
+                }
+                if (viewPortConfig.x > 0) {
+                    lastTranslate.x = -(viewPortConfig.x * viewPortConfig.scaleX);
+                } else {
+                    axisConfig.lastTranslate = {x : 0, y : 0};
+                }
+            }
+            axis._drawGridLine();
+            axis._disposeExtraProcessAndDataTableElement(elemIndex);
+        },
+        placeAxis : function (maxHeight) {
+            var axis = this,
+                axisConfig = axis.config,
+                chart = axis.chart,
+                chartConfig = chart.config,
+                components = chart.get('components'),
+                numberFormatter = components.numberFormatter,
+                smartLabel = chart.linkedItems.smartLabel,
+                labelStyle = axisConfig.labels.style,
+                vPadding = 8,
+                maxTextSize = 0,
+                spaceReturn = {
+                    top : 0,
+                    bottom : 0
+                },
+                spaceUsed = 0,
+                categories,
+                category,
+                i,
+                text,
+                smartLabelText,
+                j,
+                textStyle,
+                singleTextStyle,
+                jLen,
+                iLim,
+                maxTextDimention,
+                trendStyle = axisConfig.trend.trendStyle,
+                vtrendlines = axisConfig.vTrendLines,
+                useEllipsesWhenOverflow = axisConfig.useEllipsesWhenOverflow,
+                trendMaxHeight = 0,
+                trendSpaceUsed = 0,
+                trendObj,
+                axisSmartTrendValue,
+                trendHeight,
+                heightLeft;
+            smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
+            smartLabel.setStyle({
+                fontSize : labelStyle.fontSize,
+                fontFamily : labelStyle.fontFamily,
+                lineHeight : labelStyle.lineHeight,
+                fontWeight : labelStyle.fontWeight
+            });
+            // Used to clip the category when chart height is very small.
+            axisConfig.maxTopSpaceAvailable = chartConfig.canvasTop;
+            if (axisConfig.hasCategory) {
+                categories = axisConfig.categories.category;
+                for (i in categories) {
+                    if (!categories.hasOwnProperty(i) || i === '_attrib') {
+                        continue;
+                    }
+                    maxTextSize = 0;
+                    category = categories[i].category;
+                    for (j in category) {
+                        if (!category.hasOwnProperty(j) || j === '_attrib') {
+                            continue;
+                        }
+                        text = category[j];
+                        text.drawLabel = parseUnsafeString(text.label || text.name);
+                        textStyle = text._attrib;
+                        singleTextStyle = {
+                            fontFamily: pluck(textStyle.fontfamily, labelStyle.fontFamily).replace(/px/i, '') +
+                                PXSTRING,
+                            fontSize: pluck(textStyle.fontsize, labelStyle.fontSize),
+                            fontWeight: pluck((Number(textStyle.isbold) === 1 ? 'bold' : textStyle.isbold ===
+                                undefined ? 'bold' : undefined), labelStyle.fontWeight),
+                            fontStyle: pluck((textStyle.isitalic ? 'italic' : undefined), labelStyle.fontStyle)
+                        };
+                        setLineHeight(singleTextStyle);
+                        smartLabel.setStyle(singleTextStyle);
+                        smartLabelText = smartLabel.getOriSize(text.drawLabel);
+
+                        if (smartLabelText.height > maxTextSize) {
+                            maxTextDimention = smartLabelText;
+                            maxTextSize = smartLabelText.height;
+                        }
+                    }
+                    categories[i]._attrib.topPos = spaceUsed;
+                    spaceUsed += maxTextDimention.height + vPadding;
+                    categories[i]._attrib.bottomPos = spaceUsed;
+                }
+            }
+            heightLeft = maxHeight - spaceUsed;
+            if (axisConfig.drawTrendLines && axisConfig.drawTrendLabels && vtrendlines && axisConfig.isActive){
+                // for trend line
+                smartLabel.setStyle({
+                    fontSize : trendStyle.fontSize,
+                    fontFamily : trendStyle.fontFamily,
+                    lineHeight : trendStyle.lineHeight,
+                    fontWeight : trendStyle.fontWeight
+                });
+                axisConfig.trendBottomPadding = -1;
+                for (j = 0, jLen = vtrendlines.length; j < jLen; j += 1) {
+                    for (i = 0, iLim = vtrendlines[j].line.length; i < iLim; i += 1) {
+                        trendObj = vtrendlines[j].line[i];
+                        text = trendObj.origText || trendObj.displayvalue || trendObj.endvalue ||
+                        trendObj.startvalue || '';
+                        text = parseUnsafeString(text);
+                        // if (text === (trendObj.endvalue || trendObj.startvalue)) {
+                        //     text = ''+ numberFormatterFn.call(chartComponents.numberFormatter, text);
+                        // }
+                        trendObj.startvalue = trendObj.start && numberFormatter.getDateValue(trendObj.start).ms;
+                        trendObj.endvalue = trendObj.end && numberFormatter.getDateValue(trendObj.end).ms;
+                        trendObj.origText = text;
+                        axisSmartTrendValue = smartLabel.getSmartText(text, chart.canvasWidth,
+                            trendStyle.lineHeight, useEllipsesWhenOverflow);
+                        trendHeight = axisSmartTrendValue.height + 2;
+                        // checking if space available for trend label value to be drawn
+                        // if not make the display value empty
+                        if (heightLeft - trendHeight < 0) {
+                            trendObj.displayvalue = '';
+                        } else {
+                            // set the display value
+                            trendObj.displayvalue = axisSmartTrendValue.text;
+                            trendMaxHeight = trendMaxHeight < axisSmartTrendValue.height ?
+                                axisSmartTrendValue.height : trendMaxHeight;
+                        }
+                        // check tooltext is available which will help to draw tooltext on hover
+                        if (axisSmartTrendValue.tooltext) {
+                            trendObj.valueToolText = axisSmartTrendValue.tooltext;
+                        } else {
+                            delete trendObj.valueToolText;
+                        }
+                    }
+                }
+            }
+            axisConfig.totalHeight = spaceUsed;
+            if (trendMaxHeight > 0) {
+                trendSpaceUsed += trendMaxHeight + mathAbs(axisConfig.trendBottomPadding || 0);
+            }
+            spaceUsed = spaceUsed > maxHeight ? maxHeight : spaceUsed;
+            spaceReturn.top += spaceUsed;
+            spaceReturn.bottom += trendSpaceUsed;
+            chartConfig.categorySpaceUsed = spaceUsed;
+            return spaceReturn;
+        }
+    }, 'ganttCommon']);
+
+    FusionCharts.get ('component', ['axis', 'process', {
+        configure : function () {
+            var axis = this,
+                axisConfig = axis.config,
+                chart = axis.chart,
+                jsonData = chart.jsonData,
+                FCChartObj = jsonData.chart,
+                chartComponents = chart.components,
+                colorM = chartComponents.colorManager,
+                parent = FusionCharts.register('component', ['axis', 'cartesian']);
+
+            parent.prototype.configure.call(this);
+            //Grid properties
+            axisConfig.lineColor = convertColor (pluck (FCChartObj.gridbordercolor,
+                colorM.getColor ('gridColor')), pluckNumber (FCChartObj.gridborderalpha, 100));
+            axisConfig.lineThickness = pluckNumber (FCChartObj.gridborderthickness, 1);
+            axisConfig.lineDashStyle = pluckNumber (FCChartObj.gridborderdashed, 0) ?
+                getDashStyle (pluckNumber (FCChartObj.gridborderdashlen, 1),
+                FCChartObj.gridborderdashgap, axisConfig.lineThickness) : DASH_DEF;
+
+            axisConfig.plotLineColor = convertColor (pluck (FCChartObj.ganttlinecolor,
+                colorM.getColor ('gridColor')), pluckNumber (FCChartObj.ganttlinealpha, 100));
+            axisConfig.plotLineThickness = pluckNumber (FCChartObj.ganttlinethickness, 1);
+            axisConfig.plotLineDashStyle = pluckNumber (FCChartObj.ganttlinedashed, 0) ?
+                getDashStyle (pluckNumber (FCChartObj.ganttlinedashlen, 1),
+                FCChartObj.ganttlinedashgap, axisConfig.lineThickness) : DASH_DEF;
+
+            axisConfig.gridResizeBarColor = convertColor (pluck (FCChartObj.gridresizebarcolor,
+                colorM.getColor ('gridResizeBarColor')), pluckNumber (FCChartObj.gridresizebaralpha, 100));
+            axisConfig.gridResizeBarThickness = pluckNumber (FCChartObj.gridresizebarthickness, 1);
+            axisConfig.forceRowHeight = pluckNumber(FCChartObj.forcerowheight, 0);
+            axisConfig.rowHeight = pluckNumber(FCChartObj.rowheight, 0);
+
+            axisConfig.hoverColor = pluck (FCChartObj.processhoverbandcolor, FCChartObj.hoverbandcolor,
+                colorM.getColor ('gridColor'));
+            axisConfig.hoverAlpha = pluckNumber (FCChartObj.processhoverbandalpha, FCChartObj.hoverbandalpha, 30);
+            axisConfig.useHover = pluckNumber (FCChartObj.showprocesshoverband, FCChartObj.showhoverband,
+                FCChartObj.showhovereffect, 1);
+            axisConfig.usePlotHover = pluckNumber (FCChartObj.showganttpanehorizontalhoverband);
+            axisConfig.showFullDataTable = pluckNumber(FCChartObj.showfulldatatable, 1);
+
+            axisConfig.useVerticalScrolling = pluckNumber(FCChartObj.useverticalscrolling, 1);
+
+            axisConfig.gridLineHeaderPath = '';
+            axisConfig.gridLinePath = '';
+        },
+        setDataTable : function (dataTables) {
+            var axis = this,
+                axisConfig = axis.config;
+
+            // Initialize the category object
+            axisConfig.dataTables = {};
+            axisConfig.dataTables.dataTable = {};
+            if (dataTables) {
+                // Set the category flag to true
+                axisConfig.hasDataTables = 1;
+            } else {
+                axisConfig.hasDataTables = 0;
+                return;
+            }
+            // this will store only the category not the vline
+            extend2(axisConfig.dataTables.dataTable, dataTables);
+            dataTables = axisConfig.dataTables.dataTable;
+            // Set the final style property for each text inherating from its parent
+            axis._extractAttribToEnd(dataTables, {});
+        },
+        setProcess : function (processes) {
+            var axis = this,
+                axisConfig = axis.config,
+                axisRange = axisConfig.axisRange,
+                startPad = axisConfig.startPad || 0,
+                endPad = axisConfig.endPad || 0,
+                axisLimits,
+                processesLen,
+                processesFinal,
+                index,
+                ProcessIndex,
+                processMap;
+
+            // Initialize the category object
+            axisConfig.processes = {};
+            if (processes) {
+                // Set the category flag to true
+                axisConfig.hasProcess = 1;
+            } else {
+                axisConfig.hasProcess = 0;
+                return;
+            }
+            // this will store only the category not the vline
+            processesFinal = axisConfig.processes.process = extend2({}, processes);
+            axis._extractAttribToEnd(processesFinal, {});
+
+            processesLen = processesFinal.process.length;
+            processMap = axisConfig.processes.processMap = {};
+            axisConfig.processes.processHeightMap = {};
+            // Storing Process mapping
+            for (index = 0; index < processesLen; index += 1) {
+                ProcessIndex = processesFinal.process[index];
+                if (ProcessIndex.id) {
+                    processMap[ProcessIndex.id.toLowerCase()] = {
+                        catObj : ProcessIndex,
+                        index : index
+                    };
+                }
+            }
+
+            axisLimits = {
+                Max : processesLen - 1 + endPad,
+                Min : -startPad,
+                divGap : 1
+            };
+
+            axisRange.min = Number(toPrecision(axisLimits.Min,10));
+            axisRange.max = Number(toPrecision(axisLimits.Max, 10));
+            axisRange.tickInterval = Number(toPrecision(axisLimits.divGap, 10));
+        },
+        getProcessPositionById : function (processId) {
+            var axis = this,
+                axisConfig = axis.config,
+                process = axisConfig.processes && axisConfig.processes.processMap[processId],
+                processHeightMap = axisConfig.processes.processHeightMap;
+
+
+            if (process) {
+                return processHeightMap[process.index];
+            } else {
+                return false;
+            }
+        },
+        getProcessPositionByIndex : function (ind) {
+            var axis = this,
+                axisConfig = axis.config,
+                processHeightMap = axisConfig.processes.processHeightMap;
+
+
+            if (processHeightMap[ind]) {
+                return processHeightMap[ind];
+            } else {
+                return false;
+            }
+        },
+        setProcessHeight : function () {
+            var axis = this,
+                axisConfig = axis.config,
+                chart = axis.chart,
+                chartConfig = chart.config,
+                canvasHeight = chartConfig.canvasHeight,
+                process = axisConfig.processes.process.process,
+                processHeightMap = axisConfig.processes.processHeightMap,
+                processHeight = axisConfig.processMaxHeight,
+                height = 0,
+                forceRowHeight = axisConfig.forceRowHeight,
+                rowHeight = axisConfig.rowHeight,
+                processHeightFinal,
+                processLength,
+                i;
+            if ((processHeight * process.length) < canvasHeight || axisConfig.useVerticalScrolling === 0) {
+                processHeight = canvasHeight/process.length;
+            }
+            if (forceRowHeight === 0) {
+                if (rowHeight && rowHeight > processHeight) {
+                    processHeight = rowHeight;
+                }
+            } else {
+                processHeight = rowHeight || processHeight;
+            }
+            for (i = 0, processLength = process.length; i < processLength ; i++) {
+                processHeightFinal = pluckNumber(process[i].height,processHeight);
+                processHeightMap[i] = {
+                    top : height,
+                    bottom : height + processHeightFinal,
+                    height : processHeightFinal
+                };
+                height += processHeightFinal;
+            }
+            return height;
+        },
+        _drawComponents : function () {
+            var axis = this,
+                axisConfig = axis.config;
+
+            axis._drawProcessAndDataTable();
+            axisConfig.drawPlotlines && axis._drawPlotLine();
+            axisConfig.drawPlotBand && axis._drawPlotBand();
+            axis._drawVerticalLineAndTracker();
+            axis._drawGridLine();
+        },
+        _drawProcessAndDataTable : function () {
+            var axis = this,
+                axisConfig = axis.config,
+                chart = axis.chart,
+                axisDimention = axisConfig.axisDimention || {},
+                axisStartPosition = axisDimention.x,
+                spaceTaken = axisConfig.totalWidth || 0,
+                gridArr = axisConfig.gridArr || (axisConfig.gridArr = []),
+                canvas = axisConfig.canvas,
+                chartConfig = chart.config,
+                chartComponents = chart.components,
+                paper = chartComponents.paper,
+                canvasTop = canvas.canvasTop || chartConfig.canvasTop,
+                canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
+                canvasHeight = canvas.canvasHeight || chartConfig.canvasHeight,
+                canvasWidth = canvas.canvasWidth || chartConfig.canvasWidth,
+                layers = chart.graphics,
+                axisBottomGroup = layers.axisBottomGroup,
+                totalVisiblelWidth = axisConfig.totalVisiblelWidth,
+                i,
+                processLength,
+                dataTables,
+                dataColumn,
+                elemIndex = 0,
+                j,
+                args,
+                topBottom,
+                translateX,
+                animationDuration,
+                animObj,
+                dummyObj,
+                transposeAnimDuration,
+                animType;
+
+            animationDuration = chart.get('config', 'animationObj');
+            animObj = animationDuration.animObj;
+            dummyObj = animationDuration.dummyObj;
+            transposeAnimDuration = animationDuration.transposeAnimDuration;
+            animType = animationDuration.animType;
+
+            layers.ganttPlotHoverBandContainerParent = layers.ganttPlotHoverBandContainerParent ||
+                paper.group('guntt-plot-band-container-parent', axisBottomGroup);
+
+            if (!axisConfig.ganttPlotHoverBandContainer) {
+                axisConfig.ganttPlotHoverBandContainer = paper.group('guntt-plot-band-container',
+                    layers.ganttPlotHoverBandContainerParent);
+                axisConfig.ganttPlotHoverBandContainer.attr({
+                    'clip-rect': canvasLeft +','+ canvasTop +
+                    ','+canvasWidth+ ','+canvasHeight
+                });
+            } else {
+                axisConfig.ganttPlotHoverBandContainer.animateWith(dummyObj, animObj, {
+                    'clip-rect': canvasLeft +','+ canvasTop +
+                    ','+canvasWidth+ ','+canvasHeight
+                }, transposeAnimDuration, animType);
+            }
+            if (!axisConfig.ganttPlotLineContainer) {
+                axisConfig.ganttPlotLineContainer = paper.group('guntt-plot-line-container', axisBottomGroup);
+                axisConfig.ganttPlotLineContainer.attr({
+                    'clip-rect': canvasLeft +','+ canvasTop +
+                    ','+canvasWidth+ ','+canvasHeight
+                });
+            } else {
+                axisConfig.ganttPlotLineContainer.animateWith(dummyObj, animObj, {
+                    'clip-rect': canvasLeft +','+ canvasTop +
+                    ','+canvasWidth+ ','+canvasHeight
+                }, transposeAnimDuration, animType);
+            }
+            if (!axisConfig.headerContainer) {
+                axisConfig.headerContainer = paper.group('guntt-header-container', axisBottomGroup);
+                axisConfig.headerContainer.attr({
+                    'clip-rect': (canvasLeft - axisConfig.totalVisiblelWidth) +','+
+                    (canvasTop - chartConfig.categorySpaceUsed) +
+                    ','+axisConfig.totalVisiblelWidth+ ','+chartConfig.categorySpaceUsed
+                });
+            } else {
+                axisConfig.headerContainer.animateWith(dummyObj, animObj, {
+                    'clip-rect': (canvasLeft - axisConfig.totalVisiblelWidth) +','+
+                    (canvasTop - chartConfig.categorySpaceUsed) +
+                    ','+axisConfig.totalVisiblelWidth+ ','+chartConfig.categorySpaceUsed
+                }, transposeAnimDuration, animType);
+            }
+            if (!axisConfig.headerBackContainer) {
+                axisConfig.headerBackContainer = paper.group('guntt-header-back-container', axisConfig.headerContainer);
+            }
+            if (!axisConfig.headerLineContainer) {
+                axisConfig.headerLineContainer = paper.group('guntt-header-line-container', axisConfig.headerContainer);
+            }
+            if (!axisConfig.headerTextContainer) {
+                axisConfig.headerTextContainer = paper.group('guntt-header-text-container', axisConfig.headerContainer);
+            }
+            if (!axisConfig.labelContainer) {
+                axisConfig.labelContainer = paper.group('guntt-label-container', axisBottomGroup);
+                axisConfig.labelContainer.attr({
+                    'clip-rect': (canvasLeft - axisConfig.totalVisiblelWidth) +','+ canvasTop +
+                    ','+axisConfig.totalVisiblelWidth+ ','+canvasHeight
+                });
+            } else {
+                axisConfig.labelContainer.animateWith(dummyObj, animObj, {
+                    'clip-rect': (canvasLeft - axisConfig.totalVisiblelWidth) +','+ canvasTop +
+                    ','+axisConfig.totalVisiblelWidth+ ','+canvasHeight
+                }, transposeAnimDuration, animType);
+            }
+            if (!axisConfig.labelBackContainer) {
+                axisConfig.labelBackContainer = paper.group('guntt-label-back-container', axisConfig.labelContainer);
+            }
+            if (!axisConfig.labelLineContainer) {
+                axisConfig.labelLineContainer = paper.group('guntt-label-line-container', axisConfig.labelContainer);
+            }
+            if (!axisConfig.labelTextContainer) {
+                axisConfig.labelTextContainer = paper.group('guntt-label-text-container', axisConfig.labelContainer);
+            }
+            if (!axisConfig.hotContainer) {
+                axisConfig.hotContainer = paper.group('guntt-hot-container', axisConfig.axisLabelGroup);
+                axisConfig.hotContainer.attr({
+                    'clip-rect': (canvasLeft - axisConfig.totalVisiblelWidth) +','+
+                    (canvasTop - chartConfig.categorySpaceUsed) +
+                    ','+axisConfig.totalVisiblelWidth+ ','+(canvasHeight + chartConfig.categorySpaceUsed)
+                });
+            } else {
+                axisConfig.hotContainer.animateWith(dummyObj, animObj, {
+                    'clip-rect': (canvasLeft - axisConfig.totalVisiblelWidth) +','+
+                    (canvasTop - chartConfig.categorySpaceUsed) +
+                    ','+axisConfig.totalVisiblelWidth+ ','+(canvasHeight + chartConfig.categorySpaceUsed)
+                }, transposeAnimDuration, animType);
+            }
+
+            axisConfig.gridLinePath = '';
+            axisConfig.gridLineHeaderPath = '';
+            axisConfig.hoverElemsArr = [];
+            axisConfig.labelHoverEventName = {
+                click : 'ProcessClick',
+                rollOver : 'ProcessRollOver',
+                rollOut : 'ProcessRollOut'
+            };
+
+            if (axisConfig.hasProcess) {
+                process = axisConfig.processes.process.process;
+                args = {
+                    elem : axisConfig.processes.process,
+                    elemIndex : elemIndex,
+                    dimension : {
+                        left : axisStartPosition - spaceTaken + process._attrib.leftPos,
+                        right : axisStartPosition - spaceTaken + process._attrib.rightPos,
+                        top : canvasTop - chartConfig.categorySpaceUsed,
+                        bottom : canvasTop
+                    },
+                    type : 'header'
+                };
+                axis._drawProcessAndDataTableElement(args);
+                elemIndex += 1;
+                gridArr = axisConfig.gridArr = [];
+                for (i = 0, processLength = process.length; i < processLength ; i++) {
+                    topBottom = axis.getProcessPositionByIndex(i);
+                    args = {
+                        elem : process[i],
+                        elemIndex : elemIndex,
+                        pos : i,
+                        dimension : {
+                            left : axisStartPosition - spaceTaken + process._attrib.leftPos,
+                            right : axisStartPosition - spaceTaken + process._attrib.rightPos,
+                            top : canvasTop + topBottom.top,
+                            bottom : canvasTop + topBottom.bottom
+                        },
+                        type : 'process'
+                    };
+                    axis._drawProcessAndDataTableElement(args);
+                    elemIndex += 1;
+                    gridArr.push({y : args.dimension.bottom});
+                }
+
+            }
+            if (axisConfig.hasDataTables) {
+                dataTables = axisConfig.dataTables.dataTable.datacolumn;
+                for (i in dataTables) {
+                    if (!dataTables.hasOwnProperty(i) || i === '_attrib') {
+                        continue;
+                    }
+                    args = {
+                        elem : dataTables[i],
+                        elemIndex : elemIndex,
+                        pos : i,
+                        dimension : {
+                            left : axisStartPosition - spaceTaken + dataTables[i]._attrib.leftPos,
+                            right : axisStartPosition - spaceTaken + dataTables[i]._attrib.rightPos,
+                            top : canvasTop - chartConfig.categorySpaceUsed,
+                            bottom : canvasTop
+                        },
+                        type : 'header'
+                    };
+                    axis._drawProcessAndDataTableElement(args);
+                    elemIndex += 1;
+                    dataColumn = dataTables[i].text;
+                    for (j in dataColumn) {
+                        if (dataColumn[j]._attrib && process[j] && process[j]._attrib) {
+                            dataColumn[j]._attrib.hoverbandcolor = process[j]._attrib.hoverbandcolor;
+                            dataColumn[j]._attrib.hoverbandalpha = process[j]._attrib.hoverbandalpha;
+                            dataColumn[j]._attrib.showhoverband = process[j]._attrib.showhoverband;
+                        }
+                        if (!dataColumn.hasOwnProperty(j) || j === '_attrib') {
+                            continue;
+                        }
+                        topBottom = axis.getProcessPositionByIndex(j);
+                        args = {
+                            elem : dataColumn[j],
+                            elemIndex : elemIndex,
+                            pos : j,
+                            dimension : {
+                                left : axisStartPosition - spaceTaken + dataTables[i]._attrib.leftPos,
+                                right : axisStartPosition - spaceTaken + dataTables[i]._attrib.rightPos,
+                                top : canvasTop + topBottom.top,
+                                bottom : canvasTop + topBottom.bottom
+                            },
+                            type : 'datatable'
+                        };
+                        axis._drawProcessAndDataTableElement(args);
+                        elemIndex += 1;
+                    }
+                }
+                if (!axisConfig.drawFromProcessVlineDrag) {
+                    if (spaceTaken > totalVisiblelWidth) {
+                        translateX = (spaceTaken - totalVisiblelWidth);
+                        axis.translateAxis(translateX, undefined);
+                    } else {
+                        axis.resetTransletAxis();
+                        axis.resetTransletAxis();
+                    }
+                } else {
+                    axisConfig.drawFromProcessVlineDrag = false;
+                }
+            }
+            axis._drawGridLine();
+            axis._disposeExtraProcessAndDataTableElement(elemIndex);
+        },
+        _drawVerticalLineAndTracker : function () {
+            var axis = this,
+                axisConfig = axis.config,
+                chart = axis.chart,
+                chartComponents = chart.components,
+                canvas = axisConfig.canvas,
+                chartConfig = chart.config,
+                axisDimention = axisConfig.axisDimention || {},
+                axisStartPosition = axisDimention.x,
+                spaceTaken = axisConfig.totalWidth || 0,
+                canvasTop = canvas.canvasTop || chartConfig.canvasTop,
+                paper = chartComponents.paper,
+                plotLine = axis.components.processVline || (axis.components.processVline  = []),
+                processVlineArr = axisConfig.processVlineArr,
+                trackerGroup = axisConfig.hotContainer,
+                counter = 0,
+                TRACKER_W = 30,
+                i,
+                ln,
+                path,
+                hotElement,
+                elem,
+                top,
+                hoverLineStyle,
+                lineHotElemStyle,
+                xPos,
+                vHoverLine,
+                dragStart = function () {
+                    var ele = this,
+                        data = ele.data ('drag-options'),
+                        vHoverLine = data.vHoverLine;
+
+                    data.origX = data.lastX || (data.lastX = 0);
+                    vHoverLine.show ();
+
+                    chart.trackerClicked = true;
+                    data.draged = false;
+                },
+
+                dragMove = function (dx) {
+                    var ele = this,
+                        data = ele.data ('drag-options'),
+                        vHoverLine = data.vHoverLine,
+                        vLineSetting = data.vLineSetting,
+                        startX = vLineSetting.xPos + dx,
+                        leftSideLimit = vLineSetting.leftLimit,
+                        rightSideLimit = vLineSetting.rightLimit,
+                        transform;
+
+                    //bound limits
+                    if (startX < leftSideLimit) {
+                        dx = leftSideLimit - vLineSetting.xPos;
+                    }
+                    if (startX > rightSideLimit) {
+                        dx = rightSideLimit - vLineSetting.xPos;
+                    }
+
+                    transform = {
+                        transform: 't' + (data.origX + dx) + COMMA + 0
+                    };
+
+                    ele.attr (transform);
+                    vHoverLine.attr (transform);
+
+                    data.draged = true;
+                    data.lastX = dx;
+                },
+
+                dragUp = function () {
+                    var ele = this,
+                        data = ele.data ('drag-options'),
+                        vLineSetting = data.vLineSetting,
+                        vHoverLine = data.vHoverLine,
+                        vLineIndex = data.vLineIndex,
+                        transform;
+
+                    chart.trackerClicked = false;
+                    vHoverLine.hide ();
+                    // restoring state with respect to original state
+                    if (data.draged) {
+                        vLineSetting.left.rightPos += data.lastX || 0;
+                        vLineSetting.right.leftPos += data.lastX || 0;
+                        vLineSetting.xPos += data.lastX || 0;
+                        if (processVlineArr[vLineIndex - 1]) {
+                            processVlineArr[vLineIndex - 1].rightLimit += data.lastX || 0;
+                        }
+                        if (processVlineArr[vLineIndex + 1]) {
+                            processVlineArr[vLineIndex + 1].leftLimit += data.lastX || 0;
+                        }
+                        axisConfig.drawFromProcessVlineDrag = true;
+                        axis._drawProcessAndDataTable();
+                        axis._drawVerticalLineAndTracker();
+                        transform = {
+                            transform: 't0,0'
+                        };
+
+                        ele.attr (transform);
+                        vHoverLine.attr (transform);
+
+                    }
+                };
+
+            hoverLineStyle = {
+                stroke: axisConfig.gridResizeBarColor,
+                'stroke-width': axisConfig.gridResizeBarThickness,
+                visibility: 'hidden'
+            };
+            lineHotElemStyle = {
+                stroke: TRACKER_FILL,
+                ishot: true,
+                'stroke-width': TRACKER_W
+            };
+            top = canvasTop - chartConfig.categorySpaceUsed;
+            for(i = 0, ln = processVlineArr.length; i < ln; i += 1) {
+                if(processVlineArr[i].type === 'process') {
+                    elem = axisConfig.processes.process.process;
+                } else {
+                    elem = axisConfig.dataTables.dataTable.datacolumn[processVlineArr[i].ind];
+                }
+                xPos = axisStartPosition - spaceTaken + elem._attrib.rightPos;
+                path = ['M', xPos, top,'L', xPos, canvasTop + axisConfig.processTotalHeight];
+                if (plotLine[counter]) {
+                    vHoverLine = plotLine[counter].graphics.vHoverLine;
+                    vHoverLine.attr({path : path}).attr(hoverLineStyle);
+                    hotElement = plotLine[counter].graphics.hotElement;
+                    hotElement.attr({path : path}).attr(lineHotElemStyle);
+                } else {
+                    vHoverLine = paper.path(path, trackerGroup);
+                    vHoverLine.attr(hoverLineStyle);
+                    hotElement = paper.path(path, trackerGroup);
+                    hotElement.attr(lineHotElemStyle);
+                    plotLine[counter] = {};
+                    plotLine[counter].graphics = {};
+                    plotLine[counter].config = {};
+                    plotLine[counter].graphics.vHoverLine = vHoverLine;
+                    plotLine[counter].graphics.hotElement = hotElement;
+                }
+                hotElement.css ('cursor', R.svg && 'ew-resize' || 'e-resize')
+                .drag (dragMove,  dragStart, dragUp)
+                .data ('drag-options', {
+                    vHoverLine: plotLine[counter].graphics.vHoverLine,
+                    vLineSetting: processVlineArr[i],
+                    vLineIndex: i
+                });
+                counter += 1;
+            }
+            for (i = counter, ln = plotLine.length; i < ln; i += 1) {
+                plotLine[i].graphics.vHoverLine.attr({
+                    path : ['M',0,0]
+                });
+                plotLine[i].graphics.hotElement.attr({
+                    path : ['M',0,0]
+                });
+            }
+        },
+        manageProcessScroll : function (scroll) {
+            var axis = this,
+                axisConfig = axis.config,
+                spaceTaken = axisConfig.totalWidth || 0,
+                totalVisiblelWidth = axisConfig.totalVisiblelWidth,
+                translateX;
+
+            if (spaceTaken > totalVisiblelWidth) {
+                translateX = (spaceTaken - totalVisiblelWidth) * (1 - scroll);
+                axis.translateAxis(translateX, undefined);
+            }
+
+        },
+        placeAxis : function (maxWidth) {
+            var axis = this,
+                axisConfig = axis.config,
+                chart = axis.chart,
+                smartLabel = chart.linkedItems.smartLabel,
+                labelStyle = axisConfig.labels.style,
+                hPadding = 4,
+                processHeightPadding = 8,
+                maxProcessDimention = 0,
+                maxTextSize = 0,
+                spaceReturn = {
+                    left : 0,
+                    right : 0
+                },
+                spaceUsed = 0,
+                processOnRight = false,
+                processSpaceUsed = 0,
+                processMaxHeight = 0,
+                i,
+                text,
+                smartLabelText,
+                processLength,
+                singleProcess,
+                processStyle,
+                processParent,
+                singleProcessStyle,
+                dataTables,
+                dataTable,
+                dataColumn,
+                j,
+                textStyle,
+                singleTextStyle,
+                maxTextDimention,
+                preAlocateSpace = 0;
+            smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
+            smartLabel.setStyle({
+                fontSize : labelStyle.fontSize,
+                fontFamily : labelStyle.fontFamily,
+                lineHeight : labelStyle.lineHeight,
+                fontWeight : labelStyle.fontWeight
+            });
+            if (axisConfig.showFullDataTable === 0) {
+                preAlocateSpace = maxWidth / ((axisConfig.hasDataTables && axisConfig.dataTables &&
+                    axisConfig.dataTables.dataTable && axisConfig.dataTables.dataTable.datacolumn ?
+                    axisConfig.dataTables.dataTable.datacolumn.length :
+                    0) + 1);
+            }
+            if (axisConfig.hasProcess) {
+                process = axisConfig.processes.process.process;
+                processParent = axisConfig.processes.process;
+                if (processParent.positioningrid === 'right') {
+                    processOnRight = true;
+                }
+                if (processParent.headertext) {
+                    processParent.drawLabel = parseUnsafeString(processParent.headertext);
+                    processStyle = processParent._attrib;
+                    singleProcessStyle = {
+                        fontFamily: pluck(processStyle.headerfontfamily, labelStyle.fontFamily),
+                        fontSize: pluck(processStyle.headerfontsize, labelStyle.fontSize).replace(/px/i, '') + PXSTRING,
+                        fontWeight: pluck((Number(processStyle.headerisbold) === 1 ? 'bold' :
+                            processStyle.headerisbold === undefined ? 'bold' : undefined), labelStyle.fontWeight),
+                        fontStyle: pluck((processStyle.headerisitalic ? 'italic' : undefined), labelStyle.fontStyle)
+                    };
+                    singleProcessStyle.lineHeight = setLineHeight(singleProcessStyle);
+                    smartLabel.setStyle(singleProcessStyle);
+                    smartLabelText = smartLabel.getOriSize(processParent.drawLabel);
+
+                    if (smartLabelText.width > maxTextSize) {
+                        maxProcessDimention = smartLabelText;
+                        maxTextSize = smartLabelText.width;
+                    }
+                }
+                for (i = 0, processLength = process.length; i < processLength ; i++) {
+                    singleProcess = process[i];
+                    processStyle = singleProcess._attrib;
+                    singleProcess.drawLabel = parseUnsafeString(singleProcess.label || singleProcess.name);
+                    singleProcessStyle = {
+                        fontFamily: pluck(processStyle.fontfamily, labelStyle.fontFamily),
+                        fontSize: pluck(processStyle.fontsize, labelStyle.fontSize).replace(/px/i, '') + PXSTRING,
+                        fontWeight: pluck((processStyle.isbold ? 'bold' : undefined), labelStyle.fontWeight),
+                        fontStyle: pluck((processStyle.isitalic ? 'italic' : undefined), labelStyle.fontStyle)
+                    };
+                    singleProcessStyle.lineHeight = setLineHeight(singleProcessStyle);
+                    smartLabel.setStyle(singleProcessStyle);
+                    smartLabelText = smartLabel.getOriSize(singleProcess.drawLabel);
+
+                    if (smartLabelText.width > maxTextSize) {
+                        maxProcessDimention = smartLabelText;
+                        maxTextSize = smartLabelText.width;
+                    }
+                    if (smartLabelText.height > processMaxHeight) {
+                        processMaxHeight = smartLabelText.height;
+                    }
+                }
+                axisConfig.processMaxHeight = processMaxHeight + processHeightPadding;
+                //axisConfig.processTotalHeight = axis._setProcessHeight(processMaxHeight);
+                process._attrib.leftPos = spaceUsed;
+                if(!processOnRight) {
+                    spaceUsed += preAlocateSpace || maxProcessDimention.width + hPadding;
+                } else {
+                    processSpaceUsed = preAlocateSpace || maxProcessDimention.width + hPadding;
+                }
+                process._attrib.rightPos = spaceUsed;
+
+            }
+            if (axisConfig.hasDataTables) {
+                dataTables = axisConfig.dataTables.dataTable.datacolumn;
+                for (i in dataTables) {
+                    if (!dataTables.hasOwnProperty(i) || i === '_attrib') {
+                        continue;
+                    }
+                    dataTable = dataTables[i];
+                    maxTextSize = 0;
+                    if (dataTable.headertext) {
+                        processStyle = dataTable._attrib;
+                        dataTable.drawLabel = parseUnsafeString(dataTable.headertext);
+                        singleTextStyle = {
+                            fontFamily: pluck(processStyle.headerfontfamily, labelStyle.fontFamily),
+                            fontSize: pluck(processStyle.headerfontsize, labelStyle.fontSize).replace(/px/i, '') +
+                                PXSTRING,
+                            fontWeight: pluck((Number(processStyle.headerisbold) === 1 ? 'bold' :
+                                processStyle.headerisbold === undefined ? 'bold' : undefined), labelStyle.fontWeight),
+                            fontStyle: pluck((processStyle.headerisitalic ? 'italic' : undefined), labelStyle.fontStyle)
+                        };
+                        singleTextStyle.lineHeight = setLineHeight(singleTextStyle);
+                        smartLabel.setStyle(singleTextStyle);
+                        smartLabelText = smartLabel.getOriSize(dataTable.drawLabel);
+
+                        if (smartLabelText.width > maxTextSize) {
+                            maxTextDimention = smartLabelText;
+                            maxTextSize = smartLabelText.width;
+                        }
+                    }
+                    dataColumn = dataTable.text;
+                    for (j in dataColumn) {
+                        if (!dataColumn.hasOwnProperty(j) || j === '_attrib') {
+                            continue;
+                        }
+                        text = dataColumn[j];
+                        text.drawLabel = parseUnsafeString(text.label || text.name);
+                        textStyle = text._attrib;
+                        singleTextStyle = {
+                            fontFamily: pluck(textStyle.fontfamily, labelStyle.fontFamily),
+                            fontSize: pluck(textStyle.fontsize, labelStyle.fontSize).replace(/px/i, '') + PXSTRING,
+                            fontWeight: pluck((textStyle.isbold ? 'bold' : undefined), labelStyle.fontWeight),
+                            fontStyle: pluck((textStyle.isitalic ? 'italic' : undefined), labelStyle.fontStyle)
+                        };
+                        singleTextStyle.lineHeight = setLineHeight(singleTextStyle);
+                        smartLabel.setStyle(singleTextStyle);
+                        smartLabelText = smartLabel.getOriSize(text.drawLabel);
+
+                        if (smartLabelText.width > maxTextSize) {
+                            maxTextDimention = smartLabelText;
+                            maxTextSize = smartLabelText.width;
+                        }
+                    }
+                    dataTables[i]._attrib.leftPos = spaceUsed;
+                    spaceUsed += preAlocateSpace || maxTextDimention.width + hPadding;
+                    dataTables[i]._attrib.rightPos = spaceUsed;
+                }
+            }
+            if (axisConfig.hasProcess) {
+                if(processOnRight) {
+                    process._attrib.leftPos += spaceUsed;
+                    process._attrib.rightPos += spaceUsed + processSpaceUsed;
+                    spaceUsed += processSpaceUsed;
+                }
+            }
+            axisConfig.totalWidth = spaceUsed;
+            axis.adjustWidth();
+            spaceUsed = axisConfig.totalWidth > maxWidth ? maxWidth : axisConfig.totalWidth;
+            axisConfig.totalVisiblelWidth = spaceUsed;
+            spaceReturn.left += spaceUsed;
+            return spaceReturn;
+        },
+
+        adjustWidth : function () {
+            var axis = this,
+                axisConfig = axis.config,
+                totalWidth = axisConfig.totalWidth,
+                availableWidth = totalWidth,
+                spaceUsed = 0,
+                processOnRight = false,
+                dragPadding = 20,
+                processVlineArr,
+                attrib,
+                getWidth,
+                processParent,
+                newLeft,
+                dataTables,
+                dataTable,
+                i,
+                prevProcessVlineArr;
+
+            processVlineArr = axisConfig.processVlineArr = [];
+
+            availableWidth -= dragPadding * (axisConfig.hasDataTables && axisConfig.dataTables &&
+                axisConfig.dataTables.dataTable && axisConfig.dataTables.dataTable.datacolumn ?
+                axisConfig.dataTables.dataTable.datacolumn.length + 1 : 1);
+
+            getWidth = function (width) {
+                var retW;
+
+                availableWidth += dragPadding;
+                if (width.match(/%/g)) {
+                    retW = pluckNumber(((totalWidth * Number(width.replace(/%/g, '')/100))), 0);
+                } else {
+                    retW = pluckNumber(width, 0);
+                }
+                if (availableWidth < dragPadding) {
+                    retW = dragPadding;
+                } else if (retW > availableWidth) {
+                    retW = availableWidth;
+                }
+                availableWidth -= retW;
+                return retW;
+            };
+
+            if (axisConfig.hasProcess) {
+                process = axisConfig.processes.process.process;
+                processParent = axisConfig.processes.process;
+                if (processParent.positioningrid === 'right') {
+                    processOnRight = true;
+                }
+                attrib = process._attrib;
+                newLeft = spaceUsed;
+                spaceUsed += (getWidth(attrib.width || ''+(attrib.rightPos - attrib.leftPos)));
+                attrib.leftPos = newLeft;
+                attrib.rightPos = spaceUsed;
+
+                if (!processOnRight) {
+                    processVlineArr.push({
+                        type : 'process',
+                        ind : 0,
+                        xPos : attrib.rightPos,
+                        left : attrib,
+                        leftLimit : attrib.leftPos + dragPadding
+                    });
+                } else {
+                    spaceUsed = 0;
+                }
+
+            }
+            if (axisConfig.hasDataTables) {
+                dataTables = axisConfig.dataTables.dataTable.datacolumn;
+                for (i in dataTables) {
+                    if (!dataTables.hasOwnProperty(i) || i === '_attrib') {
+                        continue;
+                    }
+                    dataTable = dataTables[i];
+                    attrib = dataTable._attrib;
+                    newLeft = spaceUsed;
+                    spaceUsed += (getWidth(attrib.width || ''+(attrib.rightPos - attrib.leftPos)));
+                    attrib.leftPos = newLeft;
+                    attrib.rightPos = spaceUsed;
+                    prevProcessVlineArr = processVlineArr[processVlineArr.length - 1];
+                    if (prevProcessVlineArr) {
+                        prevProcessVlineArr.right = attrib;
+                        prevProcessVlineArr.rightLimit = attrib.rightPos - dragPadding;
+                    }
+                    processVlineArr.push({
+                        type : 'dataTable',
+                        ind : i,
+                        xPos : attrib.rightPos,
+                        left : attrib,
+                        leftLimit : attrib.leftPos + dragPadding
+                    });
+                }
+            }
+            if (axisConfig.hasProcess) {
+                if(processOnRight) {
+                    attrib = process._attrib;
+                    attrib.rightPos = spaceUsed + (attrib.rightPos - attrib.leftPos);
+                    attrib.leftPos = spaceUsed;
+                    spaceUsed += attrib.rightPos - attrib.leftPos;
+                    prevProcessVlineArr = processVlineArr[processVlineArr.length - 1];
+                    if (prevProcessVlineArr) {
+                        prevProcessVlineArr.right = attrib;
+                        prevProcessVlineArr.rightLimit = attrib.rightPos - dragPadding;
+                    }
+                } else {
+                    processVlineArr.pop();
+                }
+            }
+            axisConfig.totalWidth = spaceUsed;
+        }
+
+    }, 'ganttCommon']);
+
+}]);
+
+
+}));
diff --git a/fusioncharts.jqueryplugin.js b/fusioncharts.jqueryplugin.js
new file mode 100644
index 0000000..f40289a
--- /dev/null
+++ b/fusioncharts.jqueryplugin.js
@@ -0,0 +1,1486 @@
+
+(function (factory) {
+    if (typeof module === 'object' && typeof module.exports !== "undefined") {
+        module.exports = factory;
+    } else {
+        factory(FusionCharts);
+    }
+}(function (FusionCharts) {
+
+/**!
+ * @license FusionCharts JavaScript Library jQuery Plugin v1.0.4
+ * Copyright FusionCharts Technologies LLP
+ * License Information at 
+ *
+ * @author FusionCharts Technologies LLP
+ */
+/**
+ * HTMLTable Data Handler
+ * This module maps chart type alias with particular FusionCharts object.
+ * @private
+ *
+ * @module fusioncharts.transcoder.htmltable
+ */
+FusionCharts.register('module', ['private', 'HTMLTableDataHandler', function () {
+
+    var global = this,
+        win = global.window,
+        doc = win.document,
+        /**
+         *  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  element of the table. In order to support W3C
+         *  non-compliant HTML, wherein the table does not have a  containing
+         *  the  &  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  in the  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, config) {
+
+            // 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: []
+            }, i, item1, item2, categoryArr, datasetArr, chartJSON = {},
+            datasets = {}, dataObj, dataMap, labelMap, legendMap;
+
+            global.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 = global.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(global.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(global.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(global.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: global.core.transcodeData(chartJSON, 'JSON', 'XML'),
+                error: undefined
+            };
+        };
+
+    global.addDataHandler('HTMLTable', {
+        encode: function (data, obj, config) {
+            return createChartFromTable(data, obj, config);
+        },
+        decode: function (data, obj) {
+            /**
+             * @private
+             *
+             * @typedef {RuntimeException} Error-07101734
+             * @memberOf FusionCharts.debugger
+             * @group debugger-error
+             */
+            global.raiseError(obj, '07101734', 'run', '::HTMLTableDataHandler.decode()',
+                'FusionCharts HTMLTable data-handler only supports decoding of data.');
+            throw new Error('FeatureNotSupportedException()');
+        },
+        transportable: false
+    });
+}]);
+/**
+ * @module fusioncharts.jqueryplugin
+ * @private
+ * @requires fusioncharts.transcoder.htmltable
+ *
+ * @export fusioncharts.jqueryplugin.js
+ */
+FusionCharts.register('module', ['private', 'extensions.jQueryPlugin', function () {
+
+    var global = this,
+        win = global.window,
+        lib = global.hcLib,
+        doc = win.document,
+        jQ = win.jQuery,
+        renderFusionCharts,
+        captureAllFusionChartsEvents,
+        getChartObjectsFromSelection,
+        configureLinkedCharts,
+        math = win.Math,
+        mathMin = math.min,
+        isArray = lib.isArray,
+        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 = global.core;
+
+    /**
+     * 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.
+                global.core.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 (event, args) {
+        var containerElement;
+
+        // Extending our event with the jQuery event model for proper
+        // delegation and bubbling.
+        jQ.extend(event, jQ.Event('fusioncharts' + event.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);
+        }
+    };
+
+    global.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.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('
') + .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 global.core); + } + }); + +}]); + +})); diff --git a/fusioncharts.js b/fusioncharts.js new file mode 100644 index 0000000..f66f5f7 --- /dev/null +++ b/fusioncharts.js @@ -0,0 +1,89297 @@ +/* + FusionCharts JavaScript Library + Copyright FusionCharts Technologies LLP + License Information at +*/ + +(function (env, factory) { + if (typeof module === 'object' && module.exports) { + module.exports = env.document ? + factory(env) : function(win) { + if (!win.document) { + throw new Error("Window with document not present"); + } + return factory(win, true); + }; + } else { + env.FusionCharts = factory(env, true); + } +}(typeof window !== 'undefined' ? window : this, function (_window, windowExists) { + + +/** + * FusionCharts Core Framework + * This module contains the basic routines required by subsequent modules to + * extend/scale or add functionality to the FusionCharts object. + * @private + * + * @module fusioncharts.constructor + */ + /* global FusionCharts: true, _window: true, window: false */ + +if (typeof _window === 'undefined' && typeof window === 'object') { + _window = window; +} + +/* exported FusionCharts */ +var FusionCharts = (function (_window) { + + // In case FusionCharts object already exists, we skip this function. + if (_window.FusionCharts && _window.FusionCharts.version) { + return _window.FusionCharts; + } + + var win = _window, + doc = win.document, + nav = win.navigator, + + // The global variable would store all private methods and properties + // available to each module. + global = { + window: win + }, + + /** + * ~var {object} modules For maintaining module information. + */ + modules = global.modules = {}, + interpreters = global.interpreters = {}, + + objectToStringFn = Object.prototype.toString, + + isIE = /msie/i.test(nav.userAgent) && !win.opera, + hasLoaded = /loaded|complete/, + + mapLegacyDiscovered = false, // flag to keep track of legacy map script + notifyLibraryInit = function () {// function to notify init of library + var wasReady = global.ready; + global.ready = true; + if (global.raiseEvent) { + global.readyNotified = true; + /** + * This event is fired when the FusionCharts library is ready to be used. By the time this event is + * raised the browser's `DOM` is ready to be interacted with, which corresponds to the + * `DOMContentLoaded` event of browsers. In older browsers, where `DOMContentLoaded` is not fired, the + * `ready` event corresponds to the `load` event of the page. In case FusionCharts library is included + * in the page when the `DOMContentLoaded` event is already fired (i.e. script is loaded asyncronously + * using AJAX or by using script deferring methods,) the `ready` event is still fired to ensure + * integrity of all the listeners. + * + * In many ways the nature of this event is similar to `jQuery(document).ready` of jQuery library and + * `Ext.onReady` function of ExtJS library. One should interact with the FusionCharts framework (i.e. + * create new charts, set options, etc) only after this event has been fired. This event also helps you + * to neatly write your codes in separate script files and in page `` thus keeping scripts from + * being part of your page ``. + * + * An alternate (and shorthand) to subscribing the `ready` event is to use the + * {@link FusionCharts.ready} function. One advantage that {@link FusionCharts.ready} function has over + * this `ready` event is that the `ready` event is fired only once during the life-cycle of a page while + * functions passed to the {@link FusionCharts.ready} function is executed even when attached after the + * `ready` event has been fired. + * + * > This is a framework level event and as such can be only listened via + * > {@link FusionCharts.addEventLsitener} on the `FusionCharts` class alone. It will not be fired if + * > subscribed from individual chart instances. + * + * @event FusionCharts.ready + * @group framework + * @since 3.4.0 + * + * @param {array} version - The FusionCharts framework version is returned in form of an array. This is + * equivalent to the array {@link FusionCharts.version} + * @param {boolean} now - This indicates whether this event was fired at the instant of + * `window.ondomcontentloaded` event (or `window.onload` of older browsers) or whether the window was + * already loaded and this event is fired just to maintain integrity. + * + * @example + * + * + * + * + * + *
Chart loads here...
+ * + * + */ + global.raiseEvent('ready', { + version: global.core.version, + now: !wasReady + }, global.core); + } + global.readyNow = !wasReady; + }, + + FUSIONCHARTS, + + // recursive function that copies one object into another. + merge = function (obj1, obj2) { + var item, str; + //check whether obj2 is an array + //if array then iterate through it's index + //**** MOOTOOLS precution + if (obj2 instanceof Array) { + for (item = 0; item < obj2.length; item += 1) { + if (typeof obj2[item] !== 'object') { + obj1[item] = obj2[item]; + } else { + if (typeof obj1[item] !== 'object') { + obj1[item] = obj2[item] instanceof Array ? [] : {}; + } + merge(obj1[item], obj2[item]); + } + } + } + else { + for (item in obj2) { + if (typeof obj2[item] === 'object') { + str = objectToStringFn.call(obj2[item]); + if (str === '[object Object]') { + if (typeof obj1[item] !== 'object') { + obj1[item] = {}; + } + merge(obj1[item], obj2[item]); + } + else if (str === '[object Array]') { + if (!(obj1[item] instanceof Array)) { + obj1[item] = []; + } + merge(obj1[item], obj2[item]); + } + else { + obj1[item] = obj2[item]; + } + } + else { + obj1[item] = obj2[item]; + } + } + } + return obj1; + }; + + /** + * This method, when added to the prototype of an object, + * allows shallow or deep extension of the object with another + * object. + */ + global.extend = function (sink, source, proto, deep) { + var item; + // When 'proto' is marked as true, the methods and properties + // of source is not added to the prototype of the sink. + if (proto && sink.prototype) { + sink = sink.prototype; + } + + // If deep extend is specified, then we use the deep copy function + // 'merge' + if (deep === true) { + merge(sink, source); + } + // Copy all methods and properties of the object passed in parameter + // to the object to which this function is attached. + else { + for (item in source) { + sink[item] = source[item]; + } + } + + return sink; + }; + + // Function that auto-generates a unique id. + global.uniqueId = function () { + return 'chartobject-' + (global.uniqueId.lastId += 1); + }; + global.uniqueId.lastId = 0; + + // Define the policy to create default parameters for the swfObject. + // Values are in format [sourceOption, defaultValue] + // This helps in building the initial FusionCharts object when new instances + // are created from user parameters. + global.policies = { + + /** + * Contains all the customizable options that are used by the library internally and has nothing to do with + * renderer attributes, vars or parameters. + * @memberOf FusionCharts + * @type {object} + * @private + * @enum + */ + options: { + chartTypeSourcePath: ['typeSourcePath', ''], + /** @ignore **/ + product: ['product', 'v3'], + /** + * Default insert mode of adding a FusionCharts to a container + */ + insertMode: ['insertMode', 'replace'], + safeMode: ['safeMode', true], + /** + * Default parameters for overlay button + */ + overlayButton: ['overlayButton', undefined], + + containerBackgroundColor: ['containerBackgroundColor', '#ffffff'], + containerBackgroundOpacity: ['containerBackgroundOpacity', 1], + containerClassName: ['containerClassName', 'fusioncharts-container'], + + /** + * In case you want to set a default chartType for all new instances of FusionCharts + */ + chartType: ['type', undefined], + + /** + * Default styling for chart messages + */ + baseChartMessageFont: ['baseChartMessageFont', 'Verdana,sans'], + baseChartMessageFontSize: ['baseChartMessageFontSize', '10'], + baseChartMessageColor: ['baseChartMessageColor', '#666666'], + + /** + * Default position for chart messages image + */ + baseChartMessageImageHAlign: ['baseChartMessageImageHAlign', 'middle'], + baseChartMessageImageVAlign: ['baseChartMessageImageVAlign', 'middle'], + baseChartMessageImageAlpha: ['baseChartMessageImageAlpha', 100], + baseChartMessageImageScale: ['baseChartMessageImageScale', 100], + + /** + * Default message for chart (data related) + */ + dataLoadStartMessage: ['dataLoadStartMessage', 'Retrieving data. Please wait.'], + dataLoadErrorMessage: ['dataLoadErrorMessage', 'Error in loading data.'], + dataInvalidMessage: ['dataInvalidMessage', 'Invalid data.'], + dataEmptyMessage: ['dataEmptyMessage', 'No data to display.'], + + /** + * Default message for chart (chart related) + */ + typeNotSupportedMessage: ['typeNotSupportedMessage', 'Chart type not supported.'], + browserNotSupportedMessage: ['browserNotSupportedMessage', 'This browser is not supported.'], + loadMessage: ['loadMessage', 'Loading chart. Please wait.'], + renderErrorMessage: ['renderErrorMessage', 'Unable to render chart.'] + }, + /** + * ~var {object} attributes Contains configurations pertaining to the + * host (browser) environment. + */ + attributes: { + lang: ['lang', 'EN'], + id: ['id', undefined] + }, + + /** + * ~var {array} width configuration for width of the chart. + * ~var {array} height configuration for height of the chart. + * ~var {array} src specifies chart swf url + */ + width: ['width', '400'], + height: ['height', '300'], + src: ['swfUrl', ''] + }; + + // Specifies the order in which the parameters of the new + // FusionCharts objects are interpreted and converted to options object. + interpreters.stat = ['swfUrl', 'id', 'width', 'height', + 'debugMode', 'registerWithJS', 'backgroundColor', 'scaleMode', + 'lang', 'detectFlashVersion', 'autoInstallRedirect']; + + /** + * Allows the core to process an arguments object based on a set of policies + * and construct an object out of it that is mapped exactly as respective + * parameter policy defines. In other words, it uses an object and + * creates another object or updates another object with values from the + * original arguments object in a particular hierarchy and name that a set + * of rules (policies) define. + */ + global.parsePolicies = function (obj, policies, options) { + var prop, policy, value; + + // Iterate through the data policy and correspondingly create the + // three stacks of parameters, attributes and flashVars + for (policy in policies) { + + // Set just the policy object in case of single-level policy. + if (global.policies[policy] instanceof Array) { + value = options[policies[policy][0]]; + obj[policy] = value === undefined ? policies[policy][1] : value; + + } else { + + // Define objects that would hold parameters for swfobject. Also + // populate with variables from the parameters + if (typeof obj[policy] !== 'object') { + obj[policy] = {}; + } + + // Set every sub-object for two-level policy + for (prop in policies[policy]) { + value = options[policies[policy][prop][0]]; + obj[policy][prop] = value === undefined ? + policies[policy][prop][1] : value; + } + } + } + }; + + /** + * Parse commands (command interpretor) based on a specified interpreter + * structure + */ + global.parseCommands = function (obj, interpreter, args) { + var i, + l; + + if (typeof interpreter === 'string') { + interpreter = interpreters[interpreter] || []; + } + + // Iterate through the arguments template and add the keys to the + // options object while fetching corresponding values from arguments + // array. + for (i = 0, l = interpreter.length; i < l; i++) { + obj[interpreter[i]] = args[i]; + } + + return obj; + }; + + /** + * Different types of extension registrations + */ + global.registrars = { + 'module': function () { + return global.core.apply(global.core, arguments); + } + }; + + /** + * Create new instances of charts, gauges and maps using this function. + * + * The preferred way to draw charts, gauges and maps using FusionCharts is to pass the chart configurations and data + * to this constructor and call `render()` on the returned instance. You can provide all the properties, data and + * event bindings of charts through the parameters passed to this function. + * + * You can call methods of the {@link FusionCharts} class on the returned instance. For all practical purposes, this + * is the first step to creating a chart, gauges and maps using FusionCharts. + * + * __Accessing existing charts using `FusionCharts()` constructor:__ + * + * The `FusionCharts` function has a dual behavior - other than creating new charts, it can also be used to access + * already created charts. This is done by dropping the `new` operator and passing only the chart `id` as a + * parameter. For example, `var salesChart = FusionCharts('sales-chart');` will return the instance of the chart + * with the `id` "sales-chart". The previous code snippet is equivalent to + * `var salesChart = FusionCharts.items['sales-chart'];`. Refer to {@link FusionCharts.items} to know more. + * + * @constructor + * @global + * + * @param {object} options - While creating a new instance of FusionCharts, you can pass + * an `options` object with all configuration parameters for that instance. All configurations passed through this + * object are referred to as "construction parameters". Through these parameters, you can customize the look and + * feel of a chart, pass data to the chart, configure its dimensions and bind to events. + * + * Each of this object's properties correspond to a configuration option. + * + * @param {!string=} [options.type] - Provide the name of the chart type to be rendered. Full list of charts is + * available at {@tutorial setup-list-of-charts}. + * + * This parameter controls what chart will be rendered. The data passed to the chart has to be compatible with the + * chart type specified here. + * + * Alternatively, you can also call {@link FusionCharts#chartType} on the chart instance to provide the chart type. + * + * @param {!string=} [options.id] - This name is used to refer to the current instance after the + * chart has been created. The chart instance is available under this name in {@link FusionCharts.items}. If no `id` + * is provided, FusionCharts automatically generates an `id` for each chart. + * + * @param {numeric=|percent=} [options.width="400"] - Set the width in pixels or percent such as `640` or + * `'50%'`. If width is in pixels, there is no need to provide the `px` unit suffix. You can call + * {@link FusionCharts#resizeTo} function on the chart instance to set the width later on. + * + * @param {numeric=|percent=} [options.height="300"] - Set the height in pixels or percent such as `640` or + * `'50%'`. If height is in pixels, there is no need to provide the `px` unit suffix. You can call + * {@link FusionCharts#resizeTo} function on the chart instance to set the height later on. + * + * @param {string=|DOMElement=} [options.renderAt] - A chart needs reference to a DOM element on the page where + * it will be rendered. You can provide the HTML ID of the element as a string to this option, or you can pass a + * reference of the DOMElement itself where the chart needs to be rendered. + * + * For example, if you have a DOMElement like `
`, you can provide + * the value of the `div`'s `id` attribute to this option as a string. Alternatively, you can pass direct reference + * to the DOMElement like: `renderAt: document.getElementByClassName("chart-1")`. + * + * Instead of providing the DOMElement here, it can also be passed as the first parameter to the + * {@link FusionCharts.render} function. Setting the DOMElement in {@link FusionCharts.render} function overrides + * the value set here. + * + * @param {FusionCharts~dataFormats=} [options.dataFormat] - This is the name of the format of data passed to the + * `dataSource` option below. Currently, FusionCharts accepts only JSON and XML data. The value for this option is + * one of the formats specified in {@link FusionCharts~dataFormats}. + * + * @param {string=|object=} [options.dataSource] - Provide the source of data and configuration of the chart. + * FusionCharts accepts data in the formats specified in {@link FusionCharts~dataFormats}. + * + * This is the preferred way to set data and configuration of the chart. The data and configuration can also be + * updated or set using the functions {@link FusionCharts#setChartData} and {@link FusionCharts#setChartDataUrl}. + * + * @param {object=} [options.events] - You can bind multiple events to this particular chart instance through + * this option. You need to pass an object to this option, where each key is an event name fired by FusionCharts and + * value for that key is a callback in the format of {@link FusionCharts~eventListener}. + * + * To bind multiple charts to the same event, you need to use {@link FusionCharts#addEventListener} function + * instead. + * + * @param {object=} [options.link] - Provide LinkedCharts configuration. See {@link FusionCharts#configureLink} + * for details. + * + * @param {boolean=} [options.showDataLoadingMessage=false] - FusionCharts shows a message while it is retrieving + * data from a `url` provided as `dataSource`. While displaying the message the chart is grayed out and interaction + * on it is blocked. This can be prevented by setting this option to `false`. + * + * @param {boolean=} [options.showChartLoadingMessage=true] - Shows `Loading chart...` message in `renderAt` + * container if the chart needs to load additional resource/JS files. This can be turned off by setting this option + * to `false`. + * + * @param {string=} [options.baseChartMessageFont='Verdana'] - Allows to set the common custom font face for all + * chart messages. + * + * @param {string=} [options.baseChartMessageFontSize='10'] - Allows to set the common custom font size for all + * chart messages. + * + * @param {hexcolor=} [options.baseChartMessageColor='#666666'] - Allows to set the common custom font color for + * all chart messages. + * + * @param {string=} ['baseChartMessageImageHAlign'='middle'] - Allows to set the common custom horizontal + * alignment for all image as chart messages. + * + * @param {string=} ['baseChartMessageImageVAlign'='middle'] - Allows to set the common custom vertical + * alignment for all image as chart messages. + * + * @param {string=} ['baseChartMessageImageAlpha'='100'] - Allows to set the common custom alpha + * for all image as chart messages. + * + * @param {string=} ['baseChartMessageImageScale'='100'] - Allows to set the common custom scaling + * for all image as chart messages. + * + * @param {string=} [options.dataLoadStartMessage='Retrieving data. Please wait.'] - Allows to set the message to + * be displayed before the chart data begins loading. Additional properties like the font face, size, and color can + * be set by suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont, + * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are + * used. + * + * @param {string=} [options.dataLoadErrorMessage='Error in loading data.'] - Allows to set the message to be + * displayed when there is an error loading the chart data. Additional properties like the font face, size, and + * color can be set by suffixing the property name with the corresponding message key, e.g. + * dataLoadStartMessageFont, dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not + * specified, base cosmetics are used. + * + * @param {string=} [options.dataInvalidMessage='Invalid data.'] - Allows to set the message to be displayed when + * the data loaded for the chart is invalid. Additional properties like the font face, size, and color can be + * set by suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont, + * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are + * used. + * + * @param {string=} [options.dataEmptyMessage='No data to display.'] - Allows to set the message to be displayed if + * data loaded for the chart is empty. Additional properties like the font face, size, and color can be + * set by suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont, + * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are + * used. + * + * @param {string=} [options.typeNotSupportedMessage='Chart type not supported.'] - Allows to set the message to be + * displayed if specified chart type is not supported. Additional properties like the font face, size, and color + * can be set by suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont, + * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are + * used. + * + * @param {string=} [options.loadMessage='Loading chart. Please wait.'] - Allows to set the message to be displayed + * when the chart begins to load. Additional properties like the font face, size, and color can be set by + * suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont, + * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are + * used. + * + * @param {string=} [options.renderErrorMessage='Unable to render chart.'] - Allows to set the message to be + * displayed if there was an error while rendering the chart. Additional properties like the font face, size, and + * color can be set by suffixing the property name with the corresponding message key, e.g. + * dataLoadStartMessageFont, dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not + * specified, base cosmetics are used. + * + * @param {hexcolor=} [options.containerBackgroundColor="#ffffff"] - Sets the background color of the chart's + * container HTML DOM element. It is not same as `bgColor` chart attribute. To see this background color, a chart's + * own background alpha must be set to `0` by setting `bgAlpha` attribute to `"0"` in chart attributes. + * + * @param {opacity=} [options.containerBackgroundOpacity=1] - Sets the opacity of the container element. Useful + * if the chart has underlying HTML elements or background image that needs to be made visible. If opacity is + * reduced, you need to configure the chart itself to be transparent by setting the `bgAlpha` chart attribute. + * + * @param {string=} [options.containerClassName] - Sets the CSS class that will be set on the container DOM element + * of the rendered chart. Default is `fusioncharts-container`. + * + * @example + * + * + * + * + * + *
FusionCharts will load here...
+ * + * + */ + FUSIONCHARTS = function (options) { + // This point onwards, we must check whether this is being used as a + // constructor or not + if (!(this instanceof global.core)) { + // Allow private communication with modules. In case FusionCharts is + // not called as constructor and it is passed an array that is marked + // to do private communication, then share the global variable. + if (arguments.length === 1 && + options instanceof Array && options[0] === 'private') { + // Prevent overwriting and duplicate execution of modules. + if (modules[options[1]]) { + return undefined; + } + modules[options[1]] = {}; + + // Check for module-specific information + if (options[3] instanceof Array) { + global.core.version[options[1]] = options[3]; + } + + // Execute module function + if (typeof options[2] === 'function') { + return options[2].call(global, modules[options[1]]); + } + else { + return global; + } + } + + // Allow using FusionCharts object to directly access its new items + if (arguments.length === 1 && typeof options === 'string') { + return global.core.items[options]; + } + /** + * This error occurs when the `FusionCharts` constructor is called without the `new` keyword and also + * without passing a reference chart id as the parameter. In case you are rendering a new chart, ensure you + * do a `new FusionCharts({});` or in case you are using the constructor as a getter, ensure you pass the + * chart Id (string) as first parameter. + * + * @typedef {RuntimeException} Error-25081840 + * @memberOf FusionCharts.debugger + * @group debugger-error + */ + global.raiseError && global.raiseError(this, '25081840', 'run', '', + new SyntaxError('Use the "new" keyword while creating a new FusionCharts object')); + } + + // Define a variable for iterative key in various loops and the + // object variable that stores the options. + var opts = {}; + + /** + * @var {object} __state maintains internal state related information. + * @private + */ + this.__state = {}; + + // Check whether linear arguments are sent and convert it to object. + if (arguments.length === 1 && typeof arguments[0] === 'object') { + // If the above condition matches, then we can safely assume that + // the first parameter is the options object. + opts = arguments[0]; + } + else { + // Parse command interpreter policies + global.parseCommands(opts, interpreters.stat, arguments); + } + + // Incorporate the trailing object parameter as object-style + // parameter input overrides. + if (arguments.length > 1 && typeof arguments[arguments.length - 1] === 'object') { + delete opts[interpreters.stat[arguments.length - 1]]; + global.extend(opts, arguments[arguments.length - 1]); + } + + // Set autogenerated chart-id in case one is not specified + this.id = (typeof opts.id === 'undefined') ? this.id = global.uniqueId() : opts.id; + + // Set dimension passed by user and subsequently validate the options. + // - Remove trailing 'px' + this.args = opts; + + // If an item is created with same id, the previous item is disposed. + if (global.core.items[this.id] instanceof global.core) { + /** + * This error occurs when a new chart is created (instantiated) with the an `id` that has been already + * assigned to an existing chart. Change the chart Id or dispose the other chart with the duplicate id. + * + * @typedef {ParameterException} Error-06091847 + * @memberOf FusionCharts.debugger + * @group debugger-error + */ + global.raiseWarning(this, '06091847', 'param', '', + new Error('A FusionCharts object with the specified id \"' + this.id + + '\" already exists. Renaming it to ' + (this.id = global.uniqueId()))); + } + + // Parse global policies. + global.parsePolicies(this, global.policies, opts); + + // Copy chart id to attributes + this.attributes.id = this.id; + + // Set initial dimension of charts + this.resizeTo && this.resizeTo(opts.width, opts.height, true); + + // Set initial chart type of charts + this.chartType && this.chartType(opts.type || opts.swfUrl, true); + + /** + * Whenever a new instance of {@link FusionCharts} is created (as in `new FusionCharts(...)`, this pre + * initialization event is raised. This event triggers a number of modules that needs to be setup on every + * instance of FusionCharts. One can listen to this event perform actions that, on similar grounds, requires + * to be setup upn initialization of each chart. + * + * Since this event is fired upon instantiating a new FusionCharts object, it is virtually impossible to listen + * to this event by adding event listener to that individual chart. That is because, by the time one's event + * listener is attached using {@link FusionCharts#addEventListener} on the subsequent lines post doing `new + * FusionCharts(...)`, this event would have been already fired. Thus, the alternate ways to listen to this + * event are: + * + * 1. Listen to FusionCharts global events using {@link FusionCharts.addEventListener} before even creating a + * new instance. (The required instance can be identified by the `id` of the chart using + * `eventObject.sender.id`.) + * + * 2. Pass the event listener as the FusionCharts constructor parameter itself. + * + * @event FusionCharts.beforeInitialize + * @group chart + * + * @example + * // Listening using global events + * FusionCharts.addEventListener('beforeInitialize', function (opts) { + * // Prints id of the chart being rendered + * console.log("Chart with id " + opts.sender.id + " is about to be initialized."); + * }); + * + * // Pass event listener in the FusionCharts constructor + * var mychart = new FusionCharts({ + * "type": "column2d", + * "dataFormat": "json", + * "dataSource": { + * ... + * }, + * // Attach event handlers + * "events": { + * // Attach to beforeInitialize + * "beforeInitialize": function () { + * console.log("Initializing mychart..."); + * } + * } + * }); + * + * @param {numeric|percent} height - Height of the chart in pixels or percentage. + * @param {numeric|percent} width - Width of the chart in pixels or percentage. + */ + global.raiseEvent('beforeInitialize', opts, this); + + // Add this object to the repository of objects within core object. + global.core.items[this.id] = this; + + // Create alias for defaultOptions + global.core.defaultOptions = global.core.options; + + /** + * Once a new instance of {@link FusionCharts} is created and is ready to be operated upon, this `initialized` + * event is fired. Note that initialization does not indicate that the chart has been rendered. It denotes that + * the JavaScript object instance of FusionCharts is created (as in `new FusionCharts(...)` done) and is now + * ready to be operated upon (like data being passed onto it, it being rendered, etc.) + * + * @event FusionCharts.initialized + * @group chart + * + * @param {numeric|percent} height - height of the chart in pixels or percentage . + * @param {numeric|percent} width - width of the chart in pixels or percentage . + * + * @example + * // Listening using global events + * FusionCharts.addEventListener('initialized', function (opts) { + * // Prints id of the chart that has initialized + * console.log("Chart with id " + opts.sender.id + " has been initialized."); + * }); + * + * // Pass event listener in the FusionCharts constructor + * var mychart = new FusionCharts({ + * "type": "column2d", + * "dataFormat": "json", + * "dataSource": { + * ... + * }, + * // Attach event handlers + * "events": { + * // Attach to beforeInitialize + * "initialized": function () { + * console.log("Initialized mychart..."); + * } + * } + * }); + */ + global.raiseEvent('initialized', opts, this); + + return this; + }; + + // Set FusionCharts as the primary core. + global.core = FUSIONCHARTS; + + // Make the core extensible and reset the constructor of the object + // for maintaining correct prototype chain. + global.core.prototype = {}; + // Reset constructor. + global.core.prototype.constructor = global.core; + + global.extend(global.core, /** @lends FusionCharts */ { + id: 'FusionCharts', + + /** + * Specifies the framework version of {@link FusionCharts}. In the format + * `[major, minor, revision, nature, build]` + * @type {array} + */ + version: ['3', '12', '0'], + + /** + * The reference to every new instance of FusionCharts is maintained in this object with the chart `ID` as the + * key. Upon {@link FusionCharts#dispose} of the instance, the key is removed from this. One can iterate through + * all instances of {@link FusionCharts} using this object. + * + * A short-hand approach to accessing a single chart by its `id` is to use {@link FusionCharts} function itself + * but without the `new` operator and by passing the chart id as the first parameter. + * @type {object} + * + * @group framework + * + * @example + * // Assuming a page has many instances of {@link FusionCharts}, but + * // none of them are rendered, we are going to iterate through all and + * // render them. + * for (var item in FusionCharts.items) { + * FusionCharts.items[item].render(); + * } + * + * @example + * // Alternate method to access the charts using FusionCharts function to retrieve the chart from its id. + * for (var item in FusionCharts.items) { + * FusionCharts(item).render(); + * } + */ + items: {}, + + // Add an object to store options + options: {}, + + /** + * The function returns the `DOMElement` that is created inside chart container by FusionCharts. The returned + * element is the same as accessing the {@link FusionCharts#ref} property. Note that this is the `` + * element created by FusionCharts to render the chart. It is not the container element that was specified + * during rendering the chart as the `renderAt` parameter. + * + * @param {string} id - The ID of the chart, whose `DOMElement` is to be referenced. + * + * @group framework + * @since 3.1.1 + * @deprecated 3.2.0 - This method has been deprecated as direct access to `DOMElement` of the chart has become + * redundant. {@link FusionCharts#ref} property can be used in the rare case where such access to the + * `DOMElement` of a chart is required. + * + * @returns {DOMElement} + * + * @example + * // Iterate on all charts rendered on a page and move them to a common location + * var sidebar = document.getElementById('sidebar-html-div'), // assuming that your common container is this + * chart; + * + * for (chart in FusionCharts.items) { + * sidebar.appendChild(FusionCharts.getObjectReference(chart).parentNode); + * } + * + * // The above can be done without using this deprecated getObjectReference method. + * for (chart in FusionCharts.items) { + * chart = FusionCharts.items[chart]; + * chart.ref && sidebar.appendChild(chart.ref.parentNode); + * } + */ + getObjectReference: function (id) { + return global.core.items[id].ref; + }, + + /** + * Extend FusionCharts functionalities by adding modules and other items. + * @private + * + * @param {string} what - The type of extension that can be done. The possible values can be `"module"`, + * `"theme"`, etc. + * @param {...*} args - Depending upon what you want to register, supply the relevant registration items. + */ + register: function (what) { + return global.registrars[(what = (what && what.toString && what.toString().toLowerCase()))] && + global.registrars[what].apply(global.core, Array.prototype.slice.call(arguments, 1)); + }, + + /** + * Get the definations of previously registered components + * @private + * + * @param {string} what - The type of extension that can be done. The possible values can be `"component"`, + * `"theme"`, etc. + * @param {...*} args - Depending upon what you want to retirvr, supply the relevant names of sub-items. + * @todo change the defination of get so that user can't do registration using this + */ + get: function (what) { + return global.registrars[(what = (what && what.toString && what.toString().toLowerCase()))] && + global.registrars[what].apply(global.core, Array.prototype.slice.call(arguments, 1)); + } + + }); + + // Expose the core to the global scope. + win.FusionCharts = global.core; + + // Check whether legacy FusionMaps already exists at this point in execution + // time. If yes, then we need to perform routines to assimilate it. + if (win.FusionMaps && win.FusionMaps.legacy) { + global.core(['private', 'modules.core.geo', + win.FusionMaps.legacy, win.FusionMaps.version]); + mapLegacyDiscovered = true; + } + // If FusionMaps legacy was not discovered, we give it another shot after + // the page has loaded. + if (!(hasLoaded.test(doc.readyState) || doc.loaded)) { + (function () { + + var _timer, + script, + done; + + function init() { + /* jshint noarg: false */ + // quit if this function has already been called + if (done){return;} + + // flag this function so we don't do the same thing twice + done = true; + + // kill the timer + if (_timer){clearTimeout(_timer);} + + if (!mapLegacyDiscovered) { + if (win.FusionMaps && win.FusionMaps.legacy) { + global.core(['private', 'modules.core.geo', + win.FusionMaps.legacy, win.FusionMaps.version]); + } + win.FusionMaps = global.core; + } + + // Notify that library is ready for consumption. + setTimeout(notifyLibraryInit, 1); + } + + function checkInit () { + if (hasLoaded.test(doc.readyState)) { + init(); // call the onload handler + } + else { + _timer = setTimeout(checkInit, 10); + } + } + + if (doc.addEventListener) { + doc.addEventListener('DOMContentLoaded', init, false); + } + else if (doc.attachEvent) { + win.attachEvent('onLoad', init); + } + + if (isIE) { + try { + if (win.location.protocol === 'https:') { + doc.write(' + * + * + * + * + * + * + * + *
+ *
+ * + * + */ + outputTo: function (debuggerCallback) { + // Check whether the logger is a function or not. If it is a + // function, we set a reference to it to be used later as the + // logger function. + if (typeof debuggerCallback === 'function') { + /** + * The parameters passed on to the debugger callback function is in line with the value of + * {@link FusionCharts.debugger~outputFormats} specified via {@link FusionCharts.debugger.outputTo}. + * @callback FusionCharts.debugger~debuggerCallback + * + * @param {...*} outputFormatParameters - The parameters passed to the callback depend upon the + * debugger output format set. The details regarding the different variants of parameter is at + * {@link FusionCharts.debugger~outFormats}. + */ + logger.outputTo = debuggerCallback; + } + + // In case user sends 'null' as the value of the logger function, + // we can assume that user wants not to log any output. + else if (debuggerCallback === null) { + global.core[DEBUGGER].enable(false); + delete logger.outputTo; + } + }, + + /** + * The FusionCharts debugger is not enabled by default. This method allows us to enable the debugger and + * also optionally provide basic debugger configuration. + * + * The debugger works in conjunction with the browser's JavaScript console or any other special console-like + * implementation that you may have. To enable the debugger, call this function and pass a callback that + * outputs the message to the JavaScript console. The code would log the activities of every chart and the + * entire framework. + * + * ``` + * FusionCharts['debugger'].enable(true, function (message) { + * console.log(message); + * }); + * ``` + * + * If you have added this code right after including the `fusioncharts.js` script in a page that renders a + * single chart with id "myChart", your output would look somewhat like: + * + * ``` + * #1 [FusionCharts] fired "ready" event. + * #2 [myChart] fired "beforeinitialize" event. + * #3 [myChart] fired "beforedataupdate" event. + * #4 [myChart] fired "dataupdated" event. + * #5 [myChart] fired "initialized" event. + * #6 [myChart] fired "beforerender" event. + * #7 [myChart] fired "internal.loaded" event. + * #8 [myChart] fired "internal.drawstart" event. + * #9 [myChart] fired "dataloaded" event. + * #10 [myChart] fired "internal.domelementcreated" event. + * #11 [myChart] fired "loaded" event. + * #12 [myChart] fired "drawcomplete" event. + * #13 [myChart] fired "rendercomplete" event. + * ``` + * + * The output clearly shows that FusionCharts declared itself as `ready` and then the chart followed the + * routine of initialising itself, loading data, loading dependencies and then completing the rendering + * process. Had there been any error, it would have reflected in the output. + * + * > The debugger is not intended to be kept enabled on a production server since it has performance and + * > memory requirement overhead. It is meant for pre-production debugging only. + * + * @param {boolean} state - Specifies whether to enable logging of debug information. + * + * @param {FusionCharts.debugger~debuggerCallback=} [outputTo] - The function to which the debugger output + * will be passed on. + * + * @param {FusionCharts.debugger~outputFormats=} [outputFormat="text"] - Can be one of the accepted format + * names such as "text", "verbose", "event". + * + * @returns {boolean} The current 'enable' state of the debugger. + */ + enable: function (state, outputTo, outputFormat) { + + // Allow object to be sent as configuration parameter. + var config; + // In case the first parameter is object and the only parameter, + // we copy its contents to various linear parameters and save + // a copy of the object for later use. + if (typeof state === 'object' && arguments.length === 1) { + config = state; + state = config.state; + outputTo = config.outputTo; + outputFormat = config.outputFormat; + } + + // In case user send in only one parameter and that too a + // function, we can assume that he wants to use it as a logger + // function and also enable logging. + if (typeof state === 'function') { + if (typeof outputTo === 'string' && (arguments.length === 2 || config)) { + outputFormat = outputTo; + } + outputTo = state; + state = true; + } + + // In case user sends in a valid parameter to change the current + // state of the debugger, we update the debugger state. + if (typeof state === 'boolean' && state !== logger.enabled) { + global.core[(logger.enabled = state) ? 'addEventListener' + : 'removeEventListener']('*', logger.outputHandler); + } + + // If user sends in a parameter for the logger parameter, we + // set it to the logger function reference. + if (typeof outputTo === 'function') { + logger.outputTo = outputTo; + } + + // Set output format if needed. + global.core[DEBUGGER].outputFormat(outputFormat); + + // Finally send the current debugger state to the user. + return logger.enabled; + }, + + /** + * *(experimental)* This method fetches FirebugLite component's code and adds it to current page. + * Subsequently, on load of the script it enables advanced console logging to it. This is very useful for + * debugging on older Internet Explorer browsers and on mobile device browsers that do not have a JavaScript + * debugging console. + * + * Visit http://getfirebug.com/firebuglite for more information on this very popular component. This + * function fetches the script from their latest stable release channel as mentioned in + * http://getfirebug.com/firebuglite#Stable + */ + enableFirebugLite: function () { + var htmlTags; + + // Check whether firebug already exists. + /*jslint devel:true */ + if (win.console && win.console.firebug) { + // If firebug already exists, we do not need to include any + // script for firebu-lite and we simply enable logging to + //console. + global.core[DEBUGGER].enable(win.console.log, 'verbose'); + return; + } + /*jslint devel:false */ + + // Install firebug-lite within page by creating new 'script' element and appending to page head. + htmlTags = win.document.getElementsByTagName('html'); + htmlTags && htmlTags[0].setAttribute('debug', 'true'); + global.loadScript('https://getfirebug.com/firebug-lite.js#overrideConsole=false,startOpened=true', + function () { + global.core[DEBUGGER].enable(win.console.log, 'verbose'); + }, '{ startOpened: true }', true, true); + + } + }, + + /** + * @deprecated 3.4.0 - Please use FusionCharts.debugger.enable instead + * @private + */ + debugMode: { + enabled: function () { + win.setTimeout(function () { + throw new Error('Deprecated! Please use FusionCharts.debugger.enable instead.'); + }, 0); + return global.core[DEBUGGER].enable.apply(global.core[DEBUGGER], arguments); + } + } + }, false); +}]); + +/** + * This module allows FusionCharts to work with W3C Level 2 style events for + * allowing multiple handlers per event and also to do event driven development + * on a global or per-chart basis. + * @private + * + * @module fusioncharts.events + * @requires fusioncharts.constructor + * @requires fusioncharts.debugger + */ +FusionCharts.register('module', ['private', 'modules.mantle.eventmanager', function () { + + var global = this, + win = global.window, + core = global.core, + + objectProtoToString = Object.prototype.toString, + arrayToStringIdentifier = objectProtoToString.call([]), + isArray = function (obj) { + return objectProtoToString.call(obj) === arrayToStringIdentifier; + }, + + // A function to create an abstraction layer so that the try-catch / + // error suppression of flash can be avoided while raising events. + managedFnCall = function (item, scope, event, args) { + // We change the scope of the function with respect to the + // object that raised the event. + try { + item[0].call(scope, event, args || {}); + } + catch (e) { + // Call error in a separate thread to avoid stopping + // of chart load. + setTimeout(function () { + throw e; + }, 0); + } + }, + + // Function that executes all functions that are to be invoked upon trigger + // of an event. + slotLoader = function (slot, event, args) { + // If slot does not have a queue, we assume that the listener + // was never added and halt method. + if (!(slot instanceof Array)) { + // Statutory W3C NOT preventDefault flag + return; + } + + // Initialize variables. + var i = 0, scope; + + // Iterate through the slot and look for match with respect to + // type and binding. + for (; i < slot.length; i += 1) { + + // If there is a match found w.r.t. type and bind, we fire it. + if (slot[i][1] === event.sender || slot[i][1] === undefined) { + + // Determine the sender of the event for global events. + // The choice of scope differes depending on whether a + // global or a local event is being raised. + scope = slot[i][1] === event.sender ? + event.sender : global.core; + + managedFnCall(slot[i], scope, event, args); + + // Check if the user wanted to detach the event + if (event.detached === true) { + slot.splice(i, 1); + i -= 1; + event.detached = false; + } + } + + // Check whether propagation flag is set to false and discontnue + // iteration if needed. + if (event.cancelled === true) { + break; + } + } + }, + + EventTarget = { + + unpropagator: function () { + return (this.cancelled = true) === false; + }, + detacher: function () { + return (this.detached = true) === false; + }, + undefaulter: function () { + return (this.prevented = true) === false; + }, + + // Entire collection of listeners. + listeners: {}, + + // The last raised event id. Allows to calculate the next event id. + lastEventId: 0, + + addListener: function (type, listener, bind) { + + var recurseReturn, + i; + // In case type is sent as array, we recurse this function. + if (isArray(type)) { + recurseReturn = []; + // We look into each item of the 'type' parameter and send it, + // along with other parameters to a recursed addListener + // method. + for (i = 0; i < type.length; i += 1) { + recurseReturn.push(EventTarget.addListener(type[i], listener, bind)); + } + return recurseReturn; + } + + // Validate the type parameter. Listener cannot be added without + // valid type. + if (typeof type !== 'string') { + /** + * The event name has not been provided while adding an event listener. Ensure that you pass a + * `string` to the first parameter of {@link FusionCharts.addEventListener}. + * + * @typedef {ParameterException} Error-03091549 + * @memberOf FusionCharts.debugger + * @group debugger-error + */ + global.raiseError(bind || global.core, '03091549', 'param', '::EventTarget.addListener', + new Error('Unspecified Event Type')); + return; + } + + // Listener must be a function. It will not eval a string. + if (typeof listener !== 'function') { + /** + * The event listener passed to {@link FusionCharts.addEventListener} needs to be a function. + * + * @typedef {ParameterException} Error-03091550 + * @memberOf FusionCharts.debugger + * @group debugger-error + */ + global.raiseError(bind || global.core, '03091550', 'param', '::EventTarget.addListener', + new Error('Invalid Event Listener')); + return; + } + + // Desensitize the type case for user accessability. + type = type.toLowerCase(); + + // If the insertion position does not have a queue, then create one. + if (!(EventTarget.listeners[type] instanceof Array)) { + EventTarget.listeners[type] = []; + } + + // Add the listener to the queue. + EventTarget.listeners[type].push([listener, bind]); + + return listener; + }, + + removeListener: function (type, listener, bind) { + + var slot, + i; + + // Listener must be a function. Else we have nothing to remove! + if (typeof listener !== 'function') { + /** + * The event listener passed to {@link FusionCharts.removeEventListener} needs to be a function. + * Otherwise, the event listener function has no way to know which function is to be removed. + * + * @typedef {ParameterException} Error-03091560 + * @memberOf FusionCharts.debugger + * @group debugger-error + */ + global.raiseError(bind || global.core, '03091560', 'param', '::EventTarget.removeListener', + new Error('Invalid Event Listener')); + return; + } + + // In case type is sent as array, we recurse this function. + if (type instanceof Array) { + // We look into each item of the 'type' parameter and send it, + // along with other parameters to a recursed addListener + // method. + for (i = 0; i < type.length; i += 1) { + EventTarget.removeListener(type[i], listener, bind); + } + return; + } + + // Validate the type parameter. Listener cannot be removed without + // valid type. + if (typeof type !== 'string') { + /** + * The event name passed to {@link FusionCharts.removeEventListener} needs to be a string. + * + * @typedef {ParameterException} Error-03091559 + * @memberOf FusionCharts.debugger + * @group debugger-error + */ + global.raiseError(bind || global.core, '03091559', 'param', '::EventTarget.removeListener', + new Error('Unspecified Event Type')); + return; + } + + // Desensitize the type case for user accessability. + type = type.toLowerCase(); + + // Create a reference to the slot for easy lookup in this method. + slot = EventTarget.listeners[type]; + + // If slot does not have a queue, we assume that the listener + // was never added and halt method. + if (!(slot instanceof Array)) { + return; + } + + // Iterate through the slot and remove every instance of the + // event handler. + for (i = 0; i < slot.length; i += 1) { + // Remove all instances of the listener found in the queue. + if (slot[i][0] === listener && slot[i][1] === bind) { + slot.splice(i, 1); + i -= 1; + } + } + }, + + // opts can have { async:true, omni:true } + triggerEvent: function (type, sender, args, eventScope, defaultFn, cancelFn) { + + // In case, event type is missing, dispatch cannot proceed. + if (typeof type !== 'string') { + /** + * The event name passed to {@link FusionCharts.removeEventListener} needs to be a string. + * @private + * + * @typedef {ParameterException} Error-03091602 + * @memberOf FusionCharts.debugger + * @group debugger-error + */ + global.raiseError(sender, '03091602', 'param', '::EventTarget.dispatchEvent', + new Error('Invalid Event Type')); + return undefined; + } + + // Desensitize the type case for user accessability. + type = type.toLowerCase(); + + // Model the event as per W3C standards. Add the function to cancel + // event propagation by user handlers. Also append an incremental + // event id. + var eventObject = { + eventType: type, + eventId: (EventTarget.lastEventId += 1), + sender: sender || new Error('Orphan Event'), + cancelled: false, + stopPropagation: this.unpropagator, + prevented: false, + preventDefault: this.undefaulter, + detached: false, + detachHandler: this.detacher + }; + + /** + * Event listeners are used to tap into different stages of creating, updating, rendering or removing + * charts. A FusionCharts instance fires specific events based on what stage it is in. For example, the + * `renderComplete` event is fired each time a chart has finished rendering. You can listen to any such + * event using {@link FusionCharts.addEventListener} or {@link FusionCharts#addEventListener} and bind + * your own functions to that event. + * + * These functions are known as "listeners" and are passed on to the second argument (`listener`) of the + * {@link FusionCharts.addEventListener} and {@link FusionCharts#addEventListener} functions. + * + * @callback FusionCharts~eventListener + * @see FusionCharts.addEventListener + * @see FusionCharts.removeEventListener + * + * @param {object} eventObject - The first parameter passed to the listener function is an event object + * that contains all information pertaining to a particular event. + * + * @param {string} eventObject.type - The name of the event. + * + * @param {number} eventObject.eventId - A unique ID associated with the event. Internally it is an + * incrementing counter and as such can be indirectly used to verify the order in which the event was + * fired. + * + * @param {FusionCharts} eventObject.sender - The instance of FusionCharts object that fired this event. + * Occassionally, for events that are not fired by individual charts, but are fired by the framework, + * will have the framework as this property. + * + * @param {boolean} eventObject.cancelled - Shows whether an event's propagation was cancelled or not. + * It is set to `true` when `.stopPropagation()` is called. + * + * @param {function} eventObject.stopPropagation - Call this function from within a listener to prevent + * subsequent listeners from being executed. + * + * @param {boolean} eventObject.prevented - Shows whether the default action of this event has been + * prevented. It is set to `true` when `.preventDefault()` is called. + * + * @param {function} eventObject.preventDefault - Call this function to prevent the default action of an + * event. For example, for the event {@link FusionCharts#event:beforeResize}, if you do + * `.preventDefault()`, the resize will never take place and instead + * {@link FusionCharts#event:resizeCancelled} will be fired. + * + * @param {boolean} eventObject.detached - Denotes whether a listener has been detached and no longer + * gets executed for any subsequent event of this particular `type`. + * + * @param {function} eventObject.detachHandler - Allows the listener to remove itself rather than being + * called externally by {@link FusionCharts.removeEventListener}. This is very useful for one-time event + * listening or for special situations when the event is no longer required to be listened when the + * event has been fired with a specific condition. + * + * @param {object} eventArgs - Every event has an argument object as second parameter that contains + * information relevant to that particular event. + */ + slotLoader(EventTarget.listeners[type], eventObject, args); + + // Facilitate the call of a global event listener. + slotLoader(EventTarget.listeners['*'], eventObject, args); + + // Execute default action + switch (eventObject.prevented) { + case true: + if (typeof cancelFn === 'function') { + try { + cancelFn.call(eventScope || sender || win, eventObject, + args || {}); + } + catch (err) { + // Call error in a separate thread to avoid stopping + // of chart load. + setTimeout(function () { + throw err; + }, 0); + } + } + break; + default: + if (typeof defaultFn === 'function') { + try { + defaultFn.call(eventScope || sender || win, eventObject, + args || {}); + } + catch (err) { + // Call error in a separate thread to avoid stopping + // of chart load. + setTimeout(function () { + throw err; + }, 0); + } + } + } + + // Statutory W3C NOT preventDefault flag + return true; + } + }, + + // Facilitate for raising events internally. + raiseEvent = global.raiseEvent = function (type, args, obj, eventScope, + defaultFn, cancelledFn) { + return EventTarget.triggerEvent(type, obj, args, eventScope, + defaultFn, cancelledFn); + }, + + /** + * List of events that has an equivalent legacy event. Used by the + * raiseEvent method to check whether a particular event raised + * has any corresponding legacy event. + * + * @type object + */ + legacyEventList = global.legacyEventList = {}, + + /** + * Maintains a list of recently raised conditional events + * @type object + */ + conditionChecks = {}; + + global.disposeEvents = function (target) { + var type, i; + // Iterate through all events in the collection of listeners + for (type in EventTarget.listeners) { + for (i = 0; i < EventTarget.listeners[type].length; i += 1) { + // When a match is found, delete the listener from the + // collection. + if (EventTarget.listeners[type][i][1] === target) { + EventTarget.listeners[type].splice(i, 1); + } + } + } + }; + /** + * This method allows to uniformly raise events of FusionCharts + * Framework. + * + * @param {string} name specifies the name of the event to be raised. + * @param {object} args allows to provide an arguments object to be + * passed on to the event listeners. + * @param {core} obj is the FusionCharts instance object on + * behalf of which the event would be raised. + * @param {array} legacyArgs is an array of arguments to be passed on + * to the equivalent legacy event. + * @param {Event} source + * @param {function} defaultFn + * @param {function} cancelFn + * + * @type undefined + */ + global.raiseEventWithLegacy = function (name, args, obj, legacyArgs, + eventScope, defaultFn, cancelledFn) { + var legacy = legacyEventList[name]; + raiseEvent(name, args, obj, eventScope, defaultFn, cancelledFn); + if (legacy && typeof win[legacy] === 'function') { + setTimeout(function () { + win[legacy].apply(eventScope || win, legacyArgs); + }, 0); + } + }; + + /** + * This allows one to raise related events that are grouped together and + * raised by multiple sources. Usually this is used where a congregation + * of successive events need to cancel out each other and behave like a + * unified entity. + * + * @param {string} check is used to identify event groups. Provide same value + * for all events that you want to group together from multiple sources. + * @param {string} name specifies the name of the event to be raised. + * @param {object} args allows to provide an arguments object to be + * passed on to the event listeners. + * @param {core} obj is the FusionCharts instance object on + * behalf of which the event would be raised. + * @param {object} eventScope + * @param {function} defaultFn + * @param {function} cancelledFn + * + * @returns {undefined} + */ + global.raiseEventGroup = function (check, name, args, obj, eventScope, + defaultFn, cancelledFn) { + var id = obj.id, + hash = check + id; + + if (conditionChecks[hash]) { + clearTimeout(conditionChecks[hash]); + delete conditionChecks[hash]; + } + else { + if (id && hash) { + conditionChecks[hash] = setTimeout(function () { + raiseEvent(name, args, obj, eventScope, defaultFn, cancelledFn); + delete conditionChecks[hash]; + }, 0); + } + else { + raiseEvent(name, args, obj, eventScope, defaultFn, cancelledFn); + } + } + }; + + // Extend the eventlisteners to internal global. + global.addEventListener = function (type, listener) { + return EventTarget.addListener(type, listener); + }; + global.removeEventListener = function (type, listener) { + return EventTarget.removeListener(type, listener); + }; + + global.extend(core, /** @lends FusionCharts */ { + /** + * Bind callbacks to events fired throughout FusionCharts. This method can be used to listen to events across + * all FusionCharts instances on a page. + * + * An event listener is used to execute custom functions when an event is fired. FusionCharts fires events at + * all stages of creating, updating, rendering or removing a chart. This function lets you tap into any of these + * events and provide your own functions which will be called when those events are triggered. + * + * An alternative to this function is to use {@link FusionCharts#addEventListener} method on a chart instance to + * bind to an event fired by a specific chart. + * + * @param {string|array} type - The event name to listen to. The event name is not case sensitive. In case you + * want to register an event to multiple events in the same registration call, provide them as an array of event + * names. + * @param {FusionCharts~eventListener} listener - Pass the function that is to be executed when the event is + * fired. Upon an event, the listeners for that event are executed sequentially with arguments that are specific + * to that event. See {@link FusionCharts~eventListener} for more details on the arguments. + * + * @group event-handling:add + * @example + * // Show a message when a number of charts have been rendered on a page. + * FusionCharts.ready(function { + * var counter = 0, + * threshold = 3; + * + * FusionCharts.addEventListener("rendered", function (eventObject) { + * counter++; + * if (counter > threshold) { + * alert("More than " + threshold + "charts rendered!"); + * } + * }); + * }); + */ + addEventListener: function (type, listener) { + return EventTarget.addListener(type, listener); + }, + /** + * Removes an event that was originally added using {@link FusionCharts.addEventListener}. + * @param {string} type - The event name whose listener needs to be removed/detached. + * @param {function} listener - The listener function that needs to be removed. + * + * @group event-handling:remove + */ + removeEventListener: function (type, listener) { + return EventTarget.removeListener(type, listener); + }, + + /** + * This function allows to register callback functions to be executed when FusionCharts library is ready to be + * used. In general, the framework is ready after `DOMContentLoaded` browser event has been fired and all the + * initial dependent files/modules are available. One can attach multiple callbacks by calling this function any + * number of time. + * + * The callback function is executed even when attached after FusionCharts is already ready! Thus, it is + * recommended that all entry-point and initialization codes are written within this block. This also helps in + * neatly organizing all codes within a script file or the page `` and as such contextually separating + * code from HTML blocks. + * + * @param {FusionCharts~readyCallback} readyCallback - Pass a function that would be executed as callback when + * FusionCharts framework is ready. + * @param {*} [args={@link FusionCharts}] - Argument to be passed on to the callback function. + * @param {function=} [context={@link FusionCharts}] - In the situation where the function passed via `fn` + * parameter needs to be executed in a different scope than the default {@link FusionCharts} scope, pass the + * appropriate class object here. + * + * @example + * // Render a chart within a chart container `div` element. + * FusionCharts.ready(function (FusionCharts) { + * var chart = new FusionCharts({ + * type: "column2d", + * renderAt: "chart-container-div", + * dataSource: "my-chart-data.json", + * dataFormat: "jsonurl" + * }); + * // Since we are in the `ready` block, the `chart-container-div` + * // element should be available by now. + * chart.render(); + * }); + */ + ready: function (readyCallback, args, context) { + // Check if core is already ready or not. If not ready then we need to attach the function to the ready + // event listener. + if (global.ready) { + // If it is already ready, we do not need to check for readiness subsequently as this state cannot ever + // rollback. Thus we redefine the function for performant consumption post readiness. + core.ready = function (readyCallback, context) { + (typeof readyCallback === 'function') && setTimeout(function () { + /** + * The function passed as ready callback is executed when FusionCharts library is ready. Use + * {@link FusionCharts.ready} to request executing of your callback function. + * @callback FusionCharts~readyCallback + * @param {FusionCharts|*} args - By default, the parameter passed to the callback function is + * the FusionCharts library class unless specified otherwise in the `args` parameter of + * {@link FusionCharts.ready} + */ + readyCallback.call(context || core, args || core); + }, 0); + }; + core.ready(readyCallback, context); + } + else if (typeof readyCallback === 'function') { + core.addEventListener('ready', function () { + core.ready(readyCallback, args, context); + }); + } + + return this; + } + }); + core.on = core.addEventListener; // alias + + // Add eventListener extensibility to FusionCharts prototype so that individual FusionCharts objects can use + // per-chart events. + global.extend(core.prototype, /** @lends FusionCharts# */ { + /** + * Listen to events fired by an individual chart. For more information on the available events, refer to the + * events section. + * + * @param {string|string[]} type - The event name that needs to be listened to. The event name is not case + * sensitive. In case you want to register an event to multiple events in the same registration call, provide + * them as an array of event names. + * @param {FusionCharts~eventListener} listener - Pass the function that is to be executed when the event is + * fired. Upon an event, the listeners for that event are executed sequentially with arguments that are + * specific to that event. See {@link FusionCharts~eventListener} for more details on the arguments. + * + * @group event-handling:add + */ + addEventListener: function (type, listener) { + return EventTarget.addListener(type, listener, this); + }, + /** + * Removes an event that was originally added using {@link FusionCharts#addEventListener}. + * @param {string} type - The event name whose listener needs to be removed/detached. + * @param {function} listener - The listener function that needs to be removed. + * + * @group event-handling:remove + */ + removeEventListener: function (type, listener) { + return EventTarget.removeListener(type, listener, this); + } + }); + core.prototype.on = core.prototype.addEventListener; // alias + + // Add ability to parse events sent via core constructor. + global.policies.options.events = ['events', {}]; + + global.addEventListener('beforeInitialize', function (e) { + var chart = e.sender, + events = chart.options.events, + key; + + if (events) { + for (key in events) { + if (typeof events[key] === 'function') { + chart.addEventListener(key, events[key]); + } + } + } + + }); + + // Raise library initialization event, if not already done. + + if (global.ready && !global.readyNotified) { + global.readyNotified = true; + /** + * @fires FusionCharts#ready + */ + global.raiseEvent('ready', { + version: global.core.version, + now: global.readyNow + }, global.core); + } + +}]); + +/** + * This module handles all XMLHttpRequests by exposing a managed AJAX module. + * @private + * + * @module fusioncharts.ajax + * @requires fusioncharts.constructor + */ +FusionCharts.register('module', ['private', 'modules.mantle.ajax', function () { + + var global = this, + // define constants for future use. + FUNCTION = 'function', + MSXMLHTTP = 'Microsoft.XMLHTTP', + MSXMLHTTP2 = 'Msxml2.XMLHTTP', + GET = 'GET', + POST = 'POST', + XHREQERROR = 'XmlHttprequest Error', + RUN = 'run', + ERRNO = '1110111515A', + win = global.window, // keep a local reference of window scope + + // Probe IE version + version = parseFloat(win.navigator.appVersion.split('MSIE')[1]), + ielt8 = (version >= 5.5 && version <= 7) ? true : false, + firefox = /mozilla/i.test(win.navigator.userAgent), + // + // Calculate flags. + // Check whether the page is on file protocol. + fileProtocol = win.location.protocol === 'file:', + AXObject = win.ActiveXObject, + + // Check if native xhr is present + XHRNative = (!AXObject || !fileProtocol) && win.XMLHttpRequest, + + // stats + /** + * These counters keep a track of various http requests sent by FusionCharts. These can be retrieved using + * {@link FusionCharts.ajax.stats} + * @memberOf FusionCharts.ajax~ + * + * @property {number} objects - The number of internal `Ajax` objects created by FusionCharts. This is not same + * as `XMLHttpRequest` object since one `Ajax` object can have a number of `XMLHttpRequest` objects. + * @property {number} xhr - A count of all `XMLHttpRequest` objects created. + * @property {number} requests - Sum of all requests sent to server (both GET and POST.) + * @property {number} success - The total number of requests that had a successful return from server. + * @property {number} failure - The total number of requests that failed to communicate with server due to + * various factors. + * @property {number} idle - `XMLHttpRequest` objects that are idle for later use or has not been disposed yet. + */ + counters = { + objects: 0, + xhr: 0, + requests: 0, + success: 0, + failure: 0, + idle: 0 + }, + + // Prepare function to retrieve compatible xmlhttprequest. + newXmlHttpRequest = function () { + var xmlhttp; + + // if xmlhttprequest is present as native, use it. + if (XHRNative) { + newXmlHttpRequest = function () { + counters.xhr++; + return new XHRNative(); + }; + return newXmlHttpRequest(); + } + + // Use activeX for IE + try { + xmlhttp = new AXObject(MSXMLHTTP2); + newXmlHttpRequest = function () { + counters.xhr++; + return new AXObject(MSXMLHTTP2); + }; + } + catch (e) { + try { + xmlhttp = new AXObject(MSXMLHTTP); + newXmlHttpRequest = function () { + counters.xhr++; + return new AXObject(MSXMLHTTP); + }; + } + catch (e) { + xmlhttp = false; + } + } + return xmlhttp; + }, + + // Ajax class. + Ajax; + + /** + * @namespace FusionCharts.ajax + */ + global.core.ajax = /** @lends FusionCharts.ajax */ { + /** + * Returns the statistics of AJAX requests sent by FusionCharts. + * + * @param {string} [type] - Optionally, one can request a specific type of statistic by providing the type as + * one of the property-name of {@link FusionCharts.ajax~counters}. + * + * @returns {FusionCharts.ajax~counters} If no specific nature of statistics is passed to the `type` + * parameter, this function would return an object containing all counters, otherwise it returns a specific + * counter as in {@link FusionCharts.ajax~counters}. + */ + stats: function (type) { + return type ? counters[type] : global.extend({}, counters); + }, + + /** + * The default `http-headers` that are sent with every AJAX request to the server. More can be added + * and existing items can be modified. + * @enum + * @group framework + */ + headers: { + /** + * Prevents cacheing of AJAX requests. + * @type {string} + */ + 'If-Modified-Since': 'Sat, 29 Oct 1994 19:43:31 GMT', + /** + * Lets the server know that this is an AJAX request. + * @type {string} + */ + 'X-Requested-With': 'XMLHttpRequest', + /** + * Lets server know which web application is sending requests. + * @type {string} + */ + 'X-Requested-By': 'FusionCharts', + /** + * Mentions content-types that are acceptable for the response. Some servers require this for Ajax + * communication. + * @type {string} + */ + 'Accept': 'text/plain, */*', + /** + * The MIME type of the body of the request along with its charset. + * @type {string} + */ + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' + } + }; + + Ajax = global.ajax = function (success, error) { + this.onSuccess = success; + this.onError = error; + this.open = false; + counters.objects++; + counters.idle++; + }; + + global.extend(Ajax.prototype, { + headers: global.core.ajax.headers, + + transact: function (method, url, data, callbackArgs) { + var wrapper = this, + xmlhttp = wrapper.xmlhttp, + headers = wrapper.headers, + errorCallback = wrapper.onError, + successCallback = wrapper.onSuccess, + isPost = (method === POST), + postData, + xRequestedBy = 'X-Requested-By', + hasOwn = Object.prototype.hasOwnProperty, + i; + + // X-Requested-By is removed from header during cross domain ajax call + if (url.search(/^(http:\/\/|https:\/\/)/) !== -1 && + win.location.hostname !== /(http:\/\/|https:\/\/)([^\/\:]*)/.exec(url)[2]) { + // If the url does not contain http or https, then its a same domain call. No need to use regex to get + // domain. If it contains then checks domain. + delete headers[xRequestedBy]; + } + else { + !hasOwn.call(headers, xRequestedBy) && (headers[xRequestedBy] = 'FusionCharts'); + } + + if (!xmlhttp || ielt8 || firefox) { + xmlhttp = newXmlHttpRequest(); + wrapper.xmlhttp = xmlhttp; + } + + xmlhttp.onreadystatechange = function () { + try { + if (xmlhttp.readyState === 4) { + if ((!xmlhttp.status && fileProtocol) || (xmlhttp.status >= 200 && + xmlhttp.status < 300) || xmlhttp.status === 304 || + xmlhttp.status === 1223 || xmlhttp.status === 0) { + successCallback && + successCallback(xmlhttp.responseText, wrapper, + callbackArgs, url); + counters.success++; + } + else if (errorCallback) { + errorCallback(new Error(XHREQERROR), wrapper, callbackArgs, url); + counters.failure++; + } + counters.idle--; + wrapper.open = false; + } + } + catch (error) { + if (errorCallback) { + errorCallback(error, wrapper, callbackArgs, url); + } + if (win.FC_DEV_ENVIRONMENT) { + setTimeout(function () { + throw error; + }, 0); + } + counters.failure++; + } + }; + + try { + xmlhttp.open((isPost ? POST : GET), url, true); + + if (xmlhttp.overrideMimeType) { + xmlhttp.overrideMimeType('text/plain'); + } + + if (isPost) { + if (typeof data === 'string') { + postData = data; + } + else { + postData = []; + for (i in data) { + postData.push(i + '=' + (data[i] + '') + .replace(/\=/g, '%3D').replace(/\&/g, '%26')); + } + postData = postData.join('&'); + } + } + else { + postData = null; + } + for (i in headers) { + xmlhttp.setRequestHeader(i, headers[i]); + } + + xmlhttp.send(postData); + counters.requests++; + counters.idle++; + wrapper.open = true; + } + catch (e) { + /** + * AJAX runtime error. Raised when AJAX transactions raise error (security or others). The error + * message describes the nature of the error. + * + * @typedef {RuntimeException} Error-1110111515A + * @memberOf FusionCharts.debugger + * @group debugger-error + */ + global.raiseError(global.core, ERRNO, RUN, XHREQERROR, e.message); + } + + return xmlhttp; + }, + + get: function (url, callbackArgs) { + return this.transact(GET, url, undefined, callbackArgs); + }, + + post: function (url, data, callbackArgs) { + return this.transact(POST, url, data, callbackArgs); + }, + + abort: function () { + var instance = this, + xmlhttp = instance.xmlhttp; + + instance.open = false; + return xmlhttp && typeof xmlhttp.abort === FUNCTION && xmlhttp.readyState && + xmlhttp.readyState !== 0 && xmlhttp.abort(); + }, + + dispose: function () { + var instance = this; + instance.open && instance.abort(); + + delete instance.onError; + delete instance.onSuccess; + delete instance.xmlhttp; + delete instance.open; + + counters.objects--; + return (instance = null); + } + }); + +}]); + +/** + * Generic Runtime Module + * @private + * + * @module fusioncharts.runtime + * @requires fusioncharts.constructor + * @requires fusioncharts.events + * @requires fusioncharts.debugger + */ +FusionCharts.register('module', ['private', 'modules.mantle.runtime;1.1', function () { + + + var global = this, + win = global.window, + + // Set the FusionCharts filename possibilities as regular expression. + SCRIPT_NAME_REGEX = /(^|[\/\\])(fusioncharts\.js)([\?#].*)?$/ig, // jshint ignore:line + BLOCK_EXTERNAL_SCRIPT_LOADING = false, + DISALLOW_CROSSDOMAIN_RESOURCE = false, + SCRIPT_LOAD_TIMEOUTMS = 15000, + + checkBadChars = /[\\\"<>;&]/, + hasProtocolDef = /^[^\S]*?(sf|f|ht)(tp|tps):\/\//i, + FUNCTION = 'function', + + /** + * Regular Expressions that helps to check XSS security loops. + */ + LOAD_EVENTNAME = 'externalresourceload', + + /** + * To keep a track of scripts requested. + * @type Object + */ + scriptsRequested = {}, + + /** + * To keep a track of loaded script tags + */ + scriptTags = {}, + + /** + * To keep a track of scripts loaded. + * @type Object + */ + scriptsLoaded = {}, + + /** + * Keep a track of load failure check + * @type object + */ + scriptLoadFailureTimeout = {}, + + /** + * Function that safely deletes all items in a DOM element. + */ + purgeDOM = global.purgeDOM = function (d) { + var a = d.attributes, i, l, n; + if (a) { + for (i = a.length - 1; i >= 0; i -= 1) { + n = a[i].name; + if (typeof d[n] === 'function') { + d[n] = null; + } + } + } + a = d.childNodes; + if (a) { + l = a.length; + for (i = 0; i < l; i += 1) { + purgeDOM(d.childNodes[i]); + } + } + }, + + // Deconstruct policies. + // Update the arguments with latest copy of all variables by + // reverse engineering the policies. + deconstructPolicySet = function (policies, options, obj) { + var policy, + prop; + + for (policy in policies) { + + // Set just the policy object in case of single-level policy. + if (policies[policy] instanceof Array) { + options[policies[policy][0]] = obj[policy]; + } else { + // Copy the source of multi-level policies + for (prop in policies[policy]) { + options[policies[policy][prop][0]] = obj[policy][prop]; + } + } + } + }, + signatureMatchRegex = /^(FusionCharts|FusionWidgets|FusionMaps)/; + + /** + * Function to determine the script base uri for a script name + */ + global.getScriptBaseUri = function (scriptNameRegex) { + // Get a collection of all script nodes. + var scripts = win.document.getElementsByTagName('script'), + l = scripts.length, + src, + i; + + // Iterate through the script node collection and match whether its + // 'src' attribute contains fusioncharts file name. + for (i = 0; i < l; i += 1) { + src = scripts[i].getAttribute('src'); + if (!(src === undefined || src === null || + src.match(scriptNameRegex) === null)) { + return src.replace(scriptNameRegex, '$1'); + } + } + return undefined; + }; + + // Get the script base uri. (Regexp has been updated) + global.core.options.scriptBaseUri = (function () { + var baseUri = global.getScriptBaseUri(SCRIPT_NAME_REGEX); + + if (baseUri === undefined) { + /** + * FusionCharts JavaScript Library automatically determines the location where it was loaded from within + * the server. This it does by probing the ` +