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 ` +