(function (factory) { if (typeof module === 'object' && typeof module.exports !== "undefined") { module.exports = factory; } else { factory(FusionCharts); } }(function (FusionCharts) { /** * @private * @module fusioncharts.renderer.javascript.legend-gradient */ FusionCharts.register('module', ['private', 'modules.renderer.js-gradientlegend', function() { var global = this, lib = global.hcLib, pluckNumber = lib.pluckNumber, pluck = lib.pluck, toRaphaelColor = lib.toRaphaelColor, graphics = lib.graphics, dehashify = lib.dehashify, hashify = lib.hashify, convertColor = graphics.convertColor, RGBtoHex = graphics.RGBtoHex, HEXtoRGB = graphics.HEXtoRGB, getLightColor = graphics.getLightColor, getValidColor = graphics.getValidColor, compositionKeys = {}, isIE = lib.isIE, TRACKER_FILL = 'rgba(192,192,192,' + (isIE ? 0.002 : 0.000001) + ')', legendManager, DEF_COLOR = lib.COLOR_BLACK, FORMER_SLIDER_INDEX = false, LATER_SLIDER_INDEX = true, PERCENT_STR = '%', COMMA_STR = ',', hasOwnProp = ({}).hasOwnProperty, M = 'M', L = 'L', animType = 'easeIn', componentPoolFactory, universalPool = {}; // Utility functions /* * Recursively merges two objects. * Source is object from where the properties will be copied and sink is destination object where the props are to * be copied. If the property is already present in sink, it wont copy the property. * * Example: After merging the sink will be changed and returned * src = { | sink = { | sink = { * a: 1, | b: 33, | a: 1, * b: 2, | c: 44, | b: 33, * obj1: { | obj2: { | c: 44, * m: 1, | w: 11, | obj1: { * n: 2 | x: 22 | m: 1, * }, | } | n: 2 * obj2: { | | }, * x: 1, | | obj2: { * y: 2, | | w: 11, * z: 3 | | x: 22, * } | | y: 2, * } | | z: 3 * | | } * | | } * * @param: source {Object} - The object from where the props are to be copied * @param: sink {Object} - The object where the props are to be merged * @return {Object} - The reference to the sink object which was passed */ function merge (source, sink) { (function rec (source, sink) { var sourceVal, prop; for (prop in source) { // Iterates for every property in souce if (!hasOwnProp.call(source, prop)) { // Igoners if the property does not belong to the object directly, it might resides on the prototype // chain continue; } sourceVal = source[prop]; if (sink[prop] === undefined) { // Assigns the value / ref if the value of the same property is undefined (it's not checked whether // the key is directly or indirectly present on the object) in the sink. sink[prop] = sourceVal; } else if (typeof sourceVal === 'object' && typeof sourceVal !== null) { // If the value is another object recursively perform the merging. // @todo check if the value is function, array etc. If it happens this will execute unpredictively, // Its not implmented because it would more condition. rec(sourceVal, sink[prop]); } } })(source, sink); return sink; } /* * Takes care of sanity checking of the color to some extent. * If no color is passed, ie called as getValidHexColor() it returns the default color. If incorrect color is passed * it returns false. * * @param code {String} - color code in string * @return {String | Boolean} - false if invalid color is passed, otherwise the same color or default color if no * no color is passed */ function getValidHexColor (code) { var color = code ? code : DEF_COLOR; return getValidColor(color) || DEF_COLOR; } /* * Returns the opposite LIGHT color of a color. If the color is alraedy light, wont get much difference. * * @param code {String} - color code in string * @return {String} - Lightest (approx) color code of the color sent */ function getOppositeColor (code) { return getLightColor(code, 1); } function getColorBetween(range1, range2, value) { /*jshint newcap: false */ var value1 = range1.value, code1 = range1.code, rgb1 = HEXtoRGB(code1), value2 = range2.value, code2 = range2.code, rgb2 = HEXtoRGB(code2), diff, rgb; diff = value2 - value1; rgb = [ Math.round(rgb1[0] + (((rgb2[0] - rgb1[0]) / diff) * (value - value1))), Math.round(rgb1[1] + (((rgb2[1] - rgb1[1]) / diff) * (value - value1))), Math.round(rgb1[2] + (((rgb2[2] - rgb1[2]) / diff) * (value - value1))) ]; return RGBtoHex(rgb); /*jshint newcap: true */ } function normalizeFontSizeAppend(obj) { var fontSize = obj.fontSize + '', normalizeFontSize; if (!fontSize) { return obj; } normalizeFontSize = fontSize.replace(/(\d+)(px)*/, '$1px'); obj.fontSize = normalizeFontSize; return obj; } function isInvalid(arg){ if (arg === undefined || typeof arg === 'undefined' || arg === null) { return true; } else if (arg !== arg) { return true; } return false; } compositionKeys.CAPTION = 'CAPTION'; compositionKeys.LEGEND_BODY = 'LEGEND_BODY'; compositionKeys.AXIS_LABEL = 'LEGEND_LABEL'; compositionKeys.LEGEND_AXIS = 'LEGEND_AXIS'; compositionKeys.RANGE = 'RANGE'; compositionKeys.AXIS_VALUE = 'AXIS_VALUE'; legendManager = (function () { var layers, components, chart, defaultConf = {}, config; defaultConf.legendCarpetConf = { spreadFactor : 0.85, allowDrag: false, captionAlignment: 'center', padding: { v: 3, h: 3 }, style : { 'fill' : '#e4d9c1', 'stroke' : '#c4b89d' } }; defaultConf.legendCaptionConf = { spreadFactor: 0.2, padding: { v: 2, h: 2 }, style : { fill: '#786B50', fontFamily: 'sans-serif', fontSize: '12px', fontWeight: 'bold', fontStyle: 'normal' }, bound: { style: { stroke: 'none' } } }; defaultConf.legendBodyConf = { spreadFactor: 0.8, padding: { v: 2, h: 2 }, bound: { style: { stroke: 'none' } } }; defaultConf.legendAxisConf = { legendAxisHeight: 11, spreadFactor: 0.4, padding: { v: 1, h: 1 }, style : { stroke: 'none', 'stroke-opacity': 0, 'stroke-width': 1 }, line: { grooveLength: 3, offset: 8, style: { stroke: 'rgba(255, 255, 255, 0.65)', 'stroke-width': 1.5 } }, shadow : { style : { stroke : 'none', fill : toRaphaelColor({ FCcolor: { alpha : '25,0,0', angle : 360, color : '000000,FFFFFF,FFFFFF', ratio : '0,30,40' } }) } }, bound: { style: { stroke: 'none' } } }; defaultConf.sliderGroupConf = { showTooltip: 1, outerCircle : { rFactor: 1.4, style: { fill: TRACKER_FILL, stroke: '#757575', 'stroke-width': 3 } }, innerCircle : { rFactor: 0.65, style: { fill: TRACKER_FILL, stroke: '#FFFFFF' } } }; defaultConf.axisTextItemConf = { spreadFactor: 0.3, padding: { v: 1, h: 1 }, style : { fill: '#786B50', fontFamily: 'sans-serif', fontSize: '12px', fontWeight: 'normal', fontStyle: 'normal' } }; function normalizePreprocessedData (confArr) { var numberFormatter = components.numberFormatter, index, length, rawVal; for (index = 0, length = confArr.length; index < length; index++) { rawVal = confArr[index].maxvalue; if (!rawVal) { continue; } confArr[index].maxvalue = numberFormatter.getCleanValue(rawVal); } } return { init : function (options) { chart = options.chart; layers = chart.graphics; components = chart.components; }, setConf : function (conf) { config = conf; }, legacyDataParser : function (data, extremes) { var colormanagerConf = {}, numberFormatter = components.numberFormatter, colorConfArr, colorConf, startColor, endColor, index, validColor, length, colorRange, value, dispValue, mapByPercent, isMaxValPresent, obj; if (!data) { return false; } colormanagerConf.mapByPercent = mapByPercent = !!(pluckNumber(data.mapbypercent, 0)); colorConfArr = data.color || []; if (data.minvalue === undefined) { data.minvalue = extremes.min !== undefined ? (mapByPercent ? 0 : extremes.min) : 0; } if (data.maxvalue === undefined) { data.maxvalue = extremes.max !== undefined ? (mapByPercent ? 100 : extremes.max) : 100; } isMaxValPresent = false; for (index = 0, length = colorConfArr.length; index < length; index++) { if (colorConfArr[index].maxvalue) { isMaxValPresent = true; break; } } if (!isMaxValPresent) { colorConfArr = []; } startColor = data.code; colorRange = colormanagerConf.colorRange = []; colormanagerConf.gradient = !!pluckNumber(data.gradient, 1); // If no additional color array is provided as part of generating the gradient, creates the start and // end value to create the gradient if (!colorConfArr.length) { if (startColor) { // If start color is mentioned, create a gradient starting from default color to the mentioned // color endColor = getValidHexColor(startColor); startColor = getValidHexColor(); } else { // If no color is mentioned, create a gradient of two opposite color startColor = getValidHexColor(); endColor = getOppositeColor(startColor); } colorConfArr.push({ code: endColor, maxvalue: data.maxvalue, label: undefined }); } else { startColor = getValidHexColor(startColor); } normalizePreprocessedData(colorConfArr); colorConfArr = colorConfArr.sort(function (m, n) { return m.maxvalue - n.maxvalue; }); value = dispValue = data.minvalue && numberFormatter.getCleanValue(data.minvalue); dispValue = (value !== undefined || value !== null) && (mapByPercent ? value + PERCENT_STR : numberFormatter.legendValue(value)); colorRange.push({ code: dehashify(startColor), value: value, displayValue: dispValue, label: data.startlabel }); for (index = 0, length = colorConfArr.length; index < length; index++) { colorConf = colorConfArr[index]; validColor = getValidHexColor(colorConf.code || colorConf.color); value = dispValue = colorConf.maxvalue; if(isNaN(parseInt(value, 10))) { continue; } dispValue = (value !== undefined || value !== null) && (mapByPercent ? value + PERCENT_STR : numberFormatter.legendValue(value)); colorRange.push(obj = new Object({ code: dehashify(validColor), value: value, displayValue: dispValue, label: colorConf.label || colorConf.displayvalue })); } colorRange[colorRange.length - 1].label = data.endlabel || colorConf.label; return colormanagerConf; }, getDefaultConf: function (key) { return defaultConf[key]; } }; })(); componentPoolFactory = function (chart) { var chartId = chart.chartInstance.id, pool = universalPool[chartId] || (universalPool[chartId] = {}); return (function () { var elemTypes, actions = {}, paper; elemTypes = { KEY_RECT : 'rect', KEY_TEXT : 'text', KEY_GROUP: 'group', KEY_CIRCLE: 'circle', KEY_PATH: 'path' }; actions[elemTypes.KEY_RECT] = function (group) { return paper.rect(group); }; actions[elemTypes.KEY_TEXT] = function (attr, group) { return paper.text(attr, group); }; actions[elemTypes.KEY_GROUP] = function (groupName, parentGroup) { return paper.group(groupName, parentGroup); }; actions[elemTypes.KEY_CIRCLE] = function (group) { return paper.circle(group); }; actions[elemTypes.KEY_PATH] = function (pathStr, group) { return paper.path(pathStr, group); }; function _hideRecursive () { var keyLevel1, keyLevel2, objLevel1, val, index, length; for (keyLevel1 in pool) { objLevel1 = pool[keyLevel1]; for (keyLevel2 in objLevel1) { val = objLevel1[keyLevel2]; if (val instanceof Array) { for (index = 0, length = val.length; index < length; index++) { val[index] && val[index].hide(); } } else { val.hide(); } } } } return { init: function (renderer) { paper = renderer; _hideRecursive(); }, emptyPool: function () { pool = universalPool[chartId] = {}; }, getChart: function () { return chart; }, getComponent: function (id, elemType, storeTillReCall) { var idSpecificPool = pool[id], multiInstance, instance, inst, instanceRetrieved = 0; if (!idSpecificPool) { idSpecificPool = pool[id] = {}; } instance = idSpecificPool[elemType]; if ((instance && !(instance instanceof Array)) || (instance instanceof Array && instance.length > 0)) { return function () { if (storeTillReCall) { inst = instance[instanceRetrieved++]; if (inst) { return inst.show(); } else { return instance[instanceRetrieved] = actions[elemType].apply(this, arguments); } } return instance.show(); }; } return function () { if (storeTillReCall) { multiInstance = idSpecificPool[elemType] || (idSpecificPool[elemType] = []); instance = actions[elemType].apply(this, arguments); multiInstance.push(instance); return instance.show(); } return idSpecificPool[elemType] = actions[elemType].apply(this, arguments); }; }, hideAll: function () { _hideRecursive(); }, getKeys: function () { return elemTypes; } }; })(); }; function LegendBase (carpet, componentPool) { this.carpet = carpet; this._componentPool = componentPool; } LegendBase.prototype.constructor = LegendBase; LegendBase.prototype.draw = function (options) { options.componentPool = this._componentPool; return this.carpet.draw(options); }; LegendBase.prototype.getLogicalSpace = function (options, recalculate) { options.componentPool = this._componentPool; return this.carpet.getLogicalSpace(options, recalculate); }; LegendBase.prototype.dispose = function () { this.carpet && this.carpet.group && this.carpet.group.remove(); this._componentPool.emptyPool(); }; function LegendCarpet (conf) { this.conf = conf; this._id = 'GL_CARPET'; // Save the components which are the building block of the whole legend. Like: The background rect, // text, slider, color axis etc. this.compositionsByCategory = {}; this.node = undefined; this.group = undefined; this._lSpace = undefined; this.autoRecalculate = false; this.groupName = 'fc-gradient-legend'; this.moveInstructions = {}; } LegendCarpet.prototype.constructor = LegendCarpet; LegendCarpet.prototype.addCompositions = function (instance, category) { this.compositionsByCategory[category] = instance; }; LegendCarpet.prototype.getBoundingBox = function (options) { var conf = this.conf, spreadFactor = conf.spreadFactor, refSide = options.refSide, alignment = options.alignment, refOffset = options.refOffset, x = options.x, y = options.y, lWidth; lWidth = conf.width = refSide * spreadFactor; if (alignment && (x === undefined || x === null)) { x = ((refOffset + refSide) / 2) - lWidth / 2; } return { width: lWidth, height: options.maxOtherSide, x: x, y: y }; }; LegendCarpet.prototype.getPostCalcDecisions = function (bBox, componentsArea) { var conf = this.conf, padding = conf.padding, cat, totalHeightTaken = 0; for (cat in componentsArea) { totalHeightTaken += componentsArea[cat].height || 0; } bBox.height = totalHeightTaken + 2 * padding.v; }; LegendCarpet.prototype.getLogicalSpace = function (options, recalculate) { var lSpace = this._lSpace, conf = this.conf, padding = conf.padding, compositionsByCategory = this.compositionsByCategory, composition, bBox, effectivePlotArea, compositionLSPace, compositionHeight = 0, componentsArea = { }, category, compositionPlotArea, autoRecalculate, heightNotUsed = 0; if (lSpace && !recalculate) { lSpace.isImpure = true; // If logical space has already been calculated and recalculate flag is on, return without recalculating. return lSpace; } else { autoRecalculate = false; } lSpace = this._lSpace = bBox = this.getBoundingBox(options); if (isInvalid(lSpace.x) || isInvalid(lSpace.y) || isInvalid(lSpace.height) || isInvalid(lSpace.width)) { this.autoRecalculate = true; } // Copy the props effectivePlotArea = merge(bBox, {}); // The logical rect inside which the composition are drawn effectivePlotArea.height -= 2 * padding.v; effectivePlotArea.width -= 2 * padding.h; effectivePlotArea.x += padding.h; effectivePlotArea.y += padding.v; for (category in compositionsByCategory) { composition = compositionsByCategory[category]; compositionPlotArea = merge(effectivePlotArea, {}); compositionPlotArea.y += compositionHeight; compositionHeight = effectivePlotArea.height * composition.conf.spreadFactor; compositionPlotArea.height = compositionHeight + heightNotUsed; compositionLSPace = composition.getLogicalSpace(merge(compositionPlotArea, {}), options, recalculate); heightNotUsed = compositionPlotArea.height - compositionLSPace.height; componentsArea[category] = compositionLSPace; compositionHeight = compositionLSPace.height; } this.getPostCalcDecisions(bBox, componentsArea); this._lSpace = bBox; return bBox; }; LegendCarpet.prototype.setupDragging = function () { var group = this. group, dx = 0, dy = 0, idx = 0, idy = 0; group.css({cursor: 'move'}); group.drag(function (_dx, _dy) { dx = _dx; dy = _dy; group.attr({ transform: 't' + (idx + dx) + ',' + (idy + dy) }); }, function () { idx += dx; idy += dy; }, function () { }); }; LegendCarpet.prototype.draw = function (options) { var conf = this.conf, compositionsByCategory = this.compositionsByCategory, paper = options.paper, parentGroup = options.parentGroup, componentPool = options.componentPool, chart = componentPool.getChart(), group, node, category, composition, lSpace, compositionRes, animDuration = chart.get('config', 'animationObj').duration, instanceFn, keys = componentPool.getKeys(); this.getLogicalSpace(options, this.autoRecalculate); lSpace = this._lSpace; instanceFn = componentPool.getComponent(this._id, keys.KEY_GROUP); this.group = group = instanceFn(this.groupName, parentGroup); group.attr({ opacity: 0 }); group.animate({ opacity: 1 }, animDuration, animType); // The main rect outside all the composition instanceFn = componentPool.getComponent(this._id, keys.KEY_RECT); this.node = node = instanceFn(group).attr(lSpace).css(conf.style); for (category in compositionsByCategory) { composition = compositionsByCategory[category]; compositionRes = composition.draw(conf.captionAlignment, lSpace, { colorRange: options.colorRange, numberFormatter: options.numberFormatter, paper: paper, parentLayer: group, smartLabel: options.smartLabel, moveInstructions : this.moveInstructions[category], componentPool: options.componentPool }); } conf.allowDrag && this.setupDragging(); return this.node; }; function VerticalLegendCarpet () { LegendCarpet.apply(this, arguments); } VerticalLegendCarpet.prototype = Object.create(LegendCarpet.prototype); VerticalLegendCarpet.prototype.constructor = VerticalLegendCarpet; VerticalLegendCarpet.prototype.getBoundingBox = function (options) { var conf = this.conf, spreadFactor = conf.spreadFactor, refSide = options.refSide, alignment = options.alignment, refOffset = options.refOffset, x = options.x, y = options.y, lHeight; lHeight = conf.height = refSide * spreadFactor; if (alignment && (y === undefined || y === null)) { y = ((refOffset + refSide) / 2) - lHeight / 2; } return { width: options.maxOtherSide, height: lHeight, x: x, y: y }; }; VerticalLegendCarpet.prototype.getPostCalcDecisions = function (bBox, componentsArea) { var conf = this.conf, padding = conf.padding, maxWidth = Number.NEGATIVE_INFINITY, width, cat, move = this.moveInstructions, diff; LegendCarpet.prototype.getPostCalcDecisions.apply(this, arguments); for (cat in componentsArea) { width = componentsArea[cat].width; maxWidth = maxWidth < width ? width : maxWidth; } bBox.width = maxWidth + 2 * padding.h; for (cat in componentsArea) { width = componentsArea[cat].width; if (diff = maxWidth - width) { move[cat] = 't' + diff / 2 + ',0'; } } }; function LegendCaption (text, conf) { this.rawText = text; this.conf = conf; this._id = 'GL_CAPTION'; this.node = undefined; this.bound = undefined; this._lSpace = undefined; } LegendCaption.prototype.constructor = LegendCaption; LegendCaption.LEFT = { x: function (smartText, boundingBox) { return boundingBox.x + smartText.width / 2 + 2; } }; LegendCaption.RIGHT = { x: function (smartText, boundingBox) { return boundingBox.x + boundingBox.width - smartText.width / 2 - 2; } }; LegendCaption.CENTER = { x: function (smartText, boundingBox) { return boundingBox.x + (boundingBox.width / 2); } }; LegendCaption.prototype.getLogicalSpace = function (bBox, options, recalculate) { var conf = this.conf, padding = conf.padding, lSpace = this._lSpace, text = this.rawText, componentPool = options.componentPool, chart = componentPool.getChart(), smartLabel, effectivePlotArea, smartText, copyOfStyle; if (lSpace && !recalculate) { lSpace.isImpure = true; // If logical space has already been calculated and recalculate flag is on, return without recalculating. return lSpace; } lSpace = this._lSpace = { bound: { height: 0, width: 0 }, node: { logicArea: undefined, smartText: undefined } }; smartLabel = options.smartLabel; if (!text) { return lSpace.bound; } effectivePlotArea = merge(bBox, {}); // The logical rect inside which the composition are drawn effectivePlotArea.height -= 2 * padding.v; effectivePlotArea.width -= 2 * padding.h; effectivePlotArea.x += padding.h; effectivePlotArea.y += padding.v; smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow); copyOfStyle = merge(this.conf.style, {}); normalizeFontSizeAppend(copyOfStyle); smartLabel.setStyle(this._metaStyle = copyOfStyle); smartText = smartLabel.getSmartText(text, effectivePlotArea.width, effectivePlotArea.height); effectivePlotArea.height = smartText.height; effectivePlotArea.width = smartText.width; bBox.height = smartText.height + 2 * padding.v; bBox.width = smartText.width + 2 * padding.h; lSpace.node.smartText = smartText; lSpace.node.logicArea = effectivePlotArea; lSpace.bound = bBox; return bBox; }; LegendCaption.prototype.draw = function () { var conf = this.conf, smartLabel, paper, layer, boundAttr = conf.bound || {}, group, bound, instanceFn, boundingArea, lSpace, normalizedX, x, bBox, options, node, componentPool, keys; if (arguments.length >= 3) { x = arguments[0]; bBox = arguments[1]; options = arguments[2]; } else if (arguments.length >= 2){ x = arguments[0]; options = arguments[1]; } smartLabel = options.smartLabel; paper = options.paper; layer = options.parentLayer; componentPool = options.componentPool; keys = componentPool.getKeys(); // Separate group for caption and the bounding rect instanceFn = componentPool.getComponent(this._id, keys.KEY_GROUP); this.group = group = instanceFn('legend-caption', layer).css(conf.style); //group = paper.group('legend-caption', layer); this.getLogicalSpace(bBox, options); lSpace = this._lSpace; node = lSpace.node; boundingArea = lSpace.bound; // Bounding rect instanceFn = componentPool.getComponent(this._id, keys.KEY_RECT); this.bound = bound = instanceFn(group).attr(boundingArea).css(boundAttr.style); //bound = this.bound = paper.rect(boundingArea, group).css(boundAttr.style); normalizedX = typeof x === 'string' ? LegendCaption[x.toUpperCase()].x(node.smartText, bBox || node.logicArea) : x; instanceFn = componentPool.getComponent(this._id, keys.KEY_TEXT); this.node = instanceFn({ }, group).attr({ text: node.smartText.text, x: normalizedX, y: node.logicArea.y + (node.smartText.height / 2), lineHeight: this._metaStyle.lineHeight, fill: conf.style.fill }); return { group: group, bound: bound, node: this.node }; }; function LegendBody (colorRange, conf, childTextConf) { this.colorRange = colorRange; this.conf = conf; this.childTextConf = childTextConf; this._id = 'GL_BODY'; this.bound = undefined; this.compositionsByCategory = {}; this._lSpace = undefined; } // Space calculation order LegendBody.SC_STACK = [ compositionKeys.AXIS_LABEL, compositionKeys.LEGEND_AXIS, compositionKeys.AXIS_VALUE ]; LegendBody.DARW_STACK = [ compositionKeys.AXIS_VALUE, compositionKeys.LEGEND_AXIS, compositionKeys.AXIS_LABEL ]; LegendBody.prototype.constructor = LegendBody; LegendBody.prototype.addCompositions = function (instance, category) { this.compositionsByCategory[category] = instance; }; LegendBody.prototype.getCompositionPlotAreaFor = function (effectivePlotArea) { var plotArea; plotArea = merge(effectivePlotArea, {}); return function (compositionAreaOffset, sf) { compositionAreaOffset = compositionAreaOffset || {}; plotArea.y += compositionAreaOffset.height || 0; plotArea.height = effectivePlotArea.height * sf; return plotArea; }; }; LegendBody.prototype.getSpaceTaken = function (spaceObj) { return spaceObj.height; }; LegendBody.prototype.updateEffectivePlotArea = function (bBox, effectivePlotArea, val) { var conf = this.conf, padding = conf.padding; effectivePlotArea.height = val; bBox.height = val + 2 * padding.v; }; LegendBody.prototype.getLogicalSpace = function (bBox, options, recalculate) { var lSpace = this._lSpace, conf = this.conf, padding = conf.padding, compositionsByCategory = this.compositionsByCategory, composition, compositionPlotArea, compositionAreaOffset, effectivePlotArea, getCompositionPlotArea, spaceTaken = 0, index, length; if (lSpace && !recalculate) { lSpace.isImpure = true; // If logical space has already been calculated and recalculate flag is on, return without recalculating. return lSpace; } lSpace = this._lSpace = { bound: { height: 0, width: 0 }, node: { logicArea: undefined } }; effectivePlotArea = merge(bBox, {}); // The logical rect inside which the composition are drawn effectivePlotArea.height -= 2 * padding.v; effectivePlotArea.width -= 2 * padding.h; effectivePlotArea.x += padding.h; effectivePlotArea.y += padding.v; getCompositionPlotArea = this.getCompositionPlotAreaFor(effectivePlotArea); options.colorRange = this.colorRange; for (index = 0, length = LegendBody.SC_STACK.length; index < length; index++) { if (!(composition = compositionsByCategory[LegendBody.SC_STACK[index]])) { continue; } compositionPlotArea = getCompositionPlotArea(compositionAreaOffset, composition.conf.spreadFactor); compositionAreaOffset = composition.getLogicalSpace(merge(compositionPlotArea, {}), options, recalculate); spaceTaken += this.getSpaceTaken(compositionAreaOffset); } this.updateEffectivePlotArea(bBox, effectivePlotArea, spaceTaken); lSpace.node.logicArea = effectivePlotArea; lSpace.bound = bBox; return bBox; }; LegendBody.prototype.draw = function () { var childTextConf = this.childTextConf, conf = this.conf, boundStyle = conf.bound.style || {}, compositionsByCategory = this.compositionsByCategory, paper, layer, bound, colorRange, composition, legendBodyGroup, bBox, componentPool, lSpace, x, options, index, length, instanceFn, keys; if (arguments.length >= 3) { x = arguments[0]; bBox = arguments[1]; options = arguments[2]; } else if (arguments.length >= 2){ x = arguments[0]; options = arguments[1]; } paper = options.paper; layer = options.parentLayer; colorRange = options.colorRange; componentPool = options.componentPool; keys = componentPool.getKeys(); this.getLogicalSpace(bBox, options); lSpace = this._lSpace; // debugger instanceFn = componentPool.getComponent(this._id, keys.KEY_GROUP); legendBodyGroup = instanceFn('legend-body', layer).attr({ transform: 't0,0' }).css(childTextConf.style); //legendBodyGroup = paper.group('legend-body', layer); instanceFn = componentPool.getComponent(this._id, keys.KEY_RECT); this.bound = bound = instanceFn(legendBodyGroup).attr(lSpace.bound).css(boundStyle); //bound = this.bound = paper.rect(lSpace.bound, legendBodyGroup).css(boundStyle); options.colorRange = this.colorRange; options.parentLayer = legendBodyGroup; for (index = 0, length = LegendBody.DARW_STACK.length; index < length; index++) { if (!(composition = compositionsByCategory[LegendBody.DARW_STACK[index]])) { continue; } composition.draw(options); } if (options.moveInstructions) { legendBodyGroup.attr({ transform: options.moveInstructions }); } return { bound: bound, group: legendBodyGroup }; }; function VerticalLegendBody () { LegendBody.apply(this, arguments); } VerticalLegendBody.prototype = Object.create(LegendBody.prototype); VerticalLegendBody.prototype.constructor = VerticalLegendBody; VerticalLegendBody.prototype.getCompositionPlotAreaFor = function (effectivePlotArea) { var plotArea; plotArea = merge(effectivePlotArea, {}); return function (compositionAreaOffset, sf) { compositionAreaOffset = compositionAreaOffset || {}; plotArea.x += compositionAreaOffset.width || 0; plotArea.width = effectivePlotArea.width * sf; return plotArea; }; }; VerticalLegendBody.prototype.updateEffectivePlotArea = function (bBox, effectivePlotArea, val) { var conf = this.conf, padding = conf.padding; effectivePlotArea.width = val; bBox.width = val + 2 * padding.h; }; VerticalLegendBody.prototype.getSpaceTaken = function (spaceObj) { return spaceObj.width; }; function LegendLabels(conf) { this.conf = conf; this._id = 'GL_LABELS'; } LegendLabels.prototype.constructor = LegendLabels; LegendLabels.prototype.getEffectivePlotArea = function (area) { var conf = this.conf, padding = conf.padding; // The logical rect inside which the composition are drawn area.height -= 2 * padding.v; area.width -= 2 * padding.h; area.x += padding.h; area.y += padding.v; this.node = []; return area; }; LegendLabels.prototype.getLogicalSpace = function (bBox, options, recalculate) { var lSpace = this._lSpace, conf = this.conf, padding = conf.padding, cRange, smartLabel, crDataObj, index, length, labelHeights = [], leftBound, rightBound, plotArea, label, valueRatio, stop, zerothStop, lsTexts, maxHeight, effectivePlotArea, testSmartLabel, noOfLabels = 0, nextRefPoint, currPoint, leftStop, smartText, ni, copyOfStyle, componentPool = options.componentPool, chart = componentPool.getChart(), normalizedDataArr = []; if (lSpace && !recalculate) { lSpace.isImpure = true; // If logical space has already been calculated and recalculate flag is on, return without recalculating. return lSpace; } cRange = options.colorRange; smartLabel = options.smartLabel; valueRatio = cRange.getCumulativeValueRatio(); crDataObj = cRange.colorRange; lSpace = this._lSpace = { bound: { height: 0, width: 0 }, node: { logicArea: undefined, smartTexts: [] } }; lsTexts = lSpace.node.smartTexts; plotArea = merge(bBox, {}); // The logical rect inside which the composition are drawn effectivePlotArea = this.getEffectivePlotArea(plotArea); smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow); copyOfStyle = merge(conf.style, {}); normalizeFontSizeAppend(this._metaStyle = copyOfStyle); smartLabel.setStyle(copyOfStyle); testSmartLabel = smartLabel.getSmartText('W'); for (index = 0, length = crDataObj.length; index < length; index++) { label = crDataObj[index].label; if (!label) { lsTexts[index] = undefined; continue; } noOfLabels++; normalizedDataArr.push({ oriIndex: index, label: label }); } length = normalizedDataArr.length; if (length === 0) { return { height: 0, width: 0 }; } if (length > 1) { stop = ((valueRatio[normalizedDataArr[length - 1].oriIndex] - valueRatio[normalizedDataArr[0].oriIndex]) / 2) * effectivePlotArea.width / 100; } else { stop = Math.max(valueRatio[normalizedDataArr[0].oriIndex], 100 - valueRatio[normalizedDataArr[0].oriIndex]) / 2 * effectivePlotArea.width / 100; } zerothStop = stop; // first scale Label smartText = smartLabel.getSmartText(normalizedDataArr[0].label, zerothStop, effectivePlotArea.height); smartText.x = valueRatio[normalizedDataArr[0].oriIndex] * effectivePlotArea.width / 100; leftBound = smartText.x + smartText.width; labelHeights.push(smartText.height); lsTexts[normalizedDataArr[0].oriIndex] = smartText; //last scale label smartText = smartLabel.getSmartText(normalizedDataArr[length - 1].label, zerothStop, effectivePlotArea.height); smartText.x = valueRatio[normalizedDataArr[length - 1].oriIndex] * effectivePlotArea.width / 100; rightBound = smartText.x - smartText.width; labelHeights.push(smartText.height); lsTexts[normalizedDataArr[length - 1].oriIndex] = smartText; leftStop = leftBound; for (index = 1; index < length - 1; index++) { label = normalizedDataArr[index].label; ni = normalizedDataArr[index].oriIndex; smartText = undefined; nextRefPoint = index + 1 === length - 1 ? rightBound : valueRatio[normalizedDataArr[index + 1].oriIndex] * effectivePlotArea.width / 100; currPoint = valueRatio[normalizedDataArr[index].oriIndex] * effectivePlotArea.width / 100; stop = Math.min(currPoint - leftStop, nextRefPoint - currPoint); if (stop > 2 * testSmartLabel.width) { smartText = smartLabel.getSmartText(label, stop, effectivePlotArea.height); smartText.x = valueRatio[ni] * effectivePlotArea.width / 100; leftStop = stop; labelHeights.push(smartText.height); } lsTexts[normalizedDataArr[index].oriIndex] = smartText; } maxHeight = Math.max.apply(Math, labelHeights); effectivePlotArea.height = maxHeight; bBox.height = maxHeight + 2 * padding.v; lSpace.node.logicArea = effectivePlotArea; lSpace.bound = bBox; return bBox; }; LegendLabels.prototype.draw = function () { var paper, layer, bound, conf = this.conf, boundStyle = conf.bound && conf.bound.style || { stroke: 'none' }, cRange, componentPool, legendLabelsGroup, bBox, smartText, lSpace, crDataObj, valueRatio, options, index, logicArea, lsTexts, length, pos = {}, instanceFn, keys; if (arguments.length >= 2) { bBox = arguments[0]; options = arguments[1]; } else if (arguments.length >= 1){ options = arguments[0]; } paper = options.paper; layer = options.parentLayer; cRange = options.colorRange; valueRatio = cRange.getCumulativeValueRatio(); crDataObj = cRange.colorRange; componentPool = options.componentPool; keys = componentPool.getKeys(); this.getLogicalSpace(bBox, options); lSpace = this._lSpace; logicArea = lSpace.node.logicArea; lsTexts = lSpace.node.smartTexts; instanceFn = componentPool.getComponent(this._id, keys.KEY_GROUP); legendLabelsGroup = instanceFn('legend-labels', layer); instanceFn = componentPool.getComponent(this._id, keys.KEY_RECT); this.bound = bound = instanceFn(legendLabelsGroup).attr(lSpace.bound).css(boundStyle); // legendLabelsGroup.transform('R0'); instanceFn = componentPool.getComponent(this._id, keys.KEY_TEXT, true); for (index = 0, length = lsTexts.length; index < length; index++) { smartText = lsTexts[index]; if (!smartText) { continue; } pos.y = logicArea.y + smartText.height / 2; if (index === length - 1) { pos.x = logicArea.x + smartText.x - smartText.width / 2; } else if (index){ pos.x = logicArea.x + smartText.x; } else { pos.x = logicArea.x + smartText.x + smartText.width / 2; } this.node.push(instanceFn({}, legendLabelsGroup).attr({ text: smartText.text, x: pos.x, y: pos.y, lineHeight: this._metaStyle.lineHeight, fill: conf.style.fill }).transform('R0')); } return { group: legendLabelsGroup, bound: bound, node: this.node }; }; function VerticalLegendLabels () { LegendLabels.apply(this, arguments); } VerticalLegendLabels.prototype = Object.create(LegendLabels.prototype); VerticalLegendLabels.prototype.constructor = VerticalLegendLabels; VerticalLegendLabels.prototype.getLogicalSpace = function (bBox, options, recalculate) { var lSpace = this._lSpace, conf = this.conf, padding = conf.padding, cRange, smartLabel, crDataObj, index, length, labelHeights = [], leftBound, rightBound, plotArea, label, valueRatio, stop, zerothStop, lsTexts, maxHeight, effectivePlotArea, testSmartLabel, noOfLabels = 0, nextRefPoint, currPoint, leftStop, smartText, ni, copyOfStyle, componentPool = options.componentPool, chart = componentPool.getChart(), normalizedDataArr = []; if (lSpace && !recalculate) { lSpace.isImpure = true; // If logical space has already been calculated and recalculate flag is on, return without recalculating. return lSpace; } cRange = options.colorRange; smartLabel = options.smartLabel; valueRatio = cRange.getCumulativeValueRatio(); crDataObj = cRange.colorRange; lSpace = this._lSpace = { bound: { height: 0, width: 0 }, node: { logicArea: undefined, smartTexts: [] } }; lsTexts = lSpace.node.smartTexts; plotArea = merge(bBox, {}); // The logical rect inside which the composition are drawn effectivePlotArea = this.getEffectivePlotArea(plotArea); smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow); copyOfStyle = merge(conf.style, {}); normalizeFontSizeAppend(this._metaStyle = copyOfStyle); smartLabel.setStyle(copyOfStyle); testSmartLabel = smartLabel.getSmartText('W'); for (index = 0, length = crDataObj.length; index < length; index++) { label = crDataObj[index].label; if (!label) { lsTexts[index] = undefined; continue; } noOfLabels++; normalizedDataArr.push({ oriIndex: index, label: label }); } length = normalizedDataArr.length; if (length === 0) { return { height: 0, width: 0 }; } if (length > 1) { stop = ((valueRatio[normalizedDataArr[length - 1].oriIndex] - valueRatio[normalizedDataArr[0].oriIndex]) / 2) * effectivePlotArea.height / 100; } else { stop = Math.max(valueRatio[normalizedDataArr[0].oriIndex], 100 - valueRatio[normalizedDataArr[0].oriIndex]) / 2 * effectivePlotArea.height / 100; } zerothStop = stop; // first scale Label smartText = smartLabel.getSmartText(normalizedDataArr[0].label, zerothStop, effectivePlotArea.width); smartText.y = valueRatio[normalizedDataArr[0].oriIndex] * effectivePlotArea.height / 100; leftBound = smartText.y + smartText.width; labelHeights.push(smartText.height); lsTexts[normalizedDataArr[0].oriIndex] = smartText; //last scale label smartText = smartLabel.getSmartText(normalizedDataArr[length - 1].label, zerothStop, effectivePlotArea.width); smartText.y = valueRatio[normalizedDataArr[length - 1].oriIndex] * effectivePlotArea.height / 100; rightBound = smartText.y - smartText.width; labelHeights.push(smartText.height); lsTexts[normalizedDataArr[length - 1].oriIndex] = smartText; leftStop = leftBound; for (index = 1; index < length - 1; index++) { label = normalizedDataArr[index].label; ni = normalizedDataArr[index].oriIndex; smartText = undefined; nextRefPoint = index + 1 === length - 1 ? rightBound : valueRatio[normalizedDataArr[index + 1].oriIndex] * effectivePlotArea.height / 100; currPoint = valueRatio[normalizedDataArr[index].oriIndex] * effectivePlotArea.height / 100; stop = Math.min(currPoint - leftStop, nextRefPoint - currPoint); if (stop > 2 * testSmartLabel.width) { smartText = smartLabel.getSmartText(label, stop, effectivePlotArea.width); smartText.y = valueRatio[ni] * effectivePlotArea.height / 100; leftStop = stop; labelHeights.push(smartText.height); } lsTexts[normalizedDataArr[index].oriIndex] = smartText; } maxHeight = Math.max.apply(Math, labelHeights); effectivePlotArea.width = maxHeight; bBox.width = maxHeight + 2 * padding.v; lSpace.node.logicArea = effectivePlotArea; lSpace.bound = bBox; return bBox; }; VerticalLegendLabels.prototype.draw = function () { var paper, layer, bound, conf = this.conf, boundStyle = conf.bound && conf.bound.style || { stroke: 'none' }, cRange, componentPool, legendLabelsGroup, bBox, smartText, lSpace, crDataObj, valueRatio, options, index, logicArea, lsTexts, length, pos = {}, instanceFn, keys; if (arguments.length >= 2) { bBox = arguments[0]; options = arguments[1]; } else if (arguments.length >= 1){ options = arguments[0]; } paper = options.paper; layer = options.parentLayer; cRange = options.colorRange; valueRatio = cRange.getCumulativeValueRatio(); crDataObj = cRange.colorRange; componentPool = options.componentPool; keys = componentPool.getKeys(); this.getLogicalSpace(bBox, options); lSpace = this._lSpace; logicArea = lSpace.node.logicArea; lsTexts = lSpace.node.smartTexts; instanceFn = componentPool.getComponent(this._id, keys.KEY_GROUP); legendLabelsGroup = instanceFn('legend-labels', layer); instanceFn = componentPool.getComponent(this._id, keys.KEY_RECT); this.bound = bound = instanceFn(legendLabelsGroup).attr(lSpace.bound).css(boundStyle); instanceFn = componentPool.getComponent(this._id, keys.KEY_TEXT, true); for (index = 0, length = lsTexts.length; index < length; index++) { smartText = lsTexts[index]; if (!smartText) { continue; } pos.x = logicArea.x + smartText.height / 2; if (index === length - 1) { pos.y = logicArea.y + smartText.y - smartText.width / 2; } else if (index){ pos.y = logicArea.y + smartText.y; } else { pos.y = logicArea.y + smartText.y + smartText.width / 2; } this.node.push(instanceFn({}, legendLabelsGroup).attr({ text: smartText.text, x: pos.x, y: pos.y, lineHeight: this._metaStyle.lineHeight, fill: conf.style.fill }).transform('R270,' + pos.x + ',' + pos.y)); } return { group: legendLabelsGroup, bound: bound, node: this.node }; }; function LegendValues () { LegendLabels.apply(this, arguments); this._id = 'GL_VALUES'; } LegendValues.prototype = Object.create(LegendLabels.prototype); LegendValues.prototype.constructor = LegendValues; LegendValues.prototype.getLogicalSpace = function (bBox, options, recalculate) { var lSpace = this._lSpace, conf = this.conf, padding = conf.padding, componentPool = options.componentPool, chart = componentPool.getChart(), cRange, smartLabel, crDataObj, smartText, index, length, valueRatio, stop, nextRefPoint, currPoint, zerothStop, labelHeights = [], leftBound, leftStop, rightBound, maxHeight, effectivePlotArea, val, dispValue, testSmartLabel, copyOfStyle, lsTexts; if (lSpace && !recalculate) { lSpace.isImpure = true; // If logical space has already been calculated and recalculate flag is on, return without recalculating. return lSpace; } cRange = options.colorRange; smartLabel = options.smartLabel; crDataObj = cRange.colorRange; valueRatio = cRange.getCumulativeValueRatio(); lSpace = this._lSpace = { bound: { height: 0, width: 0 }, node: { logicArea: undefined, smartTexts: [] } }; lsTexts = lSpace.node.smartTexts; effectivePlotArea = merge(bBox, {}); // The logical rect inside which the composition are drawn effectivePlotArea.height -= 2 * padding.v; effectivePlotArea.width -= 2 * padding.h; effectivePlotArea.x += padding.h; effectivePlotArea.y += padding.v; smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow); copyOfStyle = merge(conf.style, {}); normalizeFontSizeAppend(this._metaStyle = copyOfStyle); smartLabel.setStyle(copyOfStyle); testSmartLabel = smartLabel.getSmartText('W'); length = crDataObj.length; zerothStop = stop = ((valueRatio[length - 1] - valueRatio[0]) / 2) * effectivePlotArea.width / 100; // first scale Label dispValue = crDataObj[0].displayValue; smartText = smartLabel.getSmartText((typeof dispValue !== 'string' && dispValue !== undefined) && dispValue.toString() || dispValue, zerothStop, effectivePlotArea.height); smartText.x = valueRatio[0] * effectivePlotArea.width / 100; leftBound = smartText.x + smartText.width; labelHeights.push(smartText.height); lsTexts[0] = smartText; //last scale label smartText = smartLabel.getSmartText(crDataObj[length - 1].displayValue, zerothStop, effectivePlotArea.height); smartText.x = valueRatio[length - 1] * effectivePlotArea.width / 100; rightBound = smartText.x - smartText.width; labelHeights.push(smartText.height); lsTexts[length - 1] = smartText; leftStop = leftBound; for (index = 1; index < length - 1; index++) { smartText = undefined; val = crDataObj[index].displayValue; nextRefPoint = index + 1 === length - 1 ? rightBound : valueRatio[index + 1] * effectivePlotArea.width / 100; currPoint = valueRatio[index] * effectivePlotArea.width / 100; stop = Math.min(currPoint - leftStop, nextRefPoint - currPoint); if (stop > 1.5 * testSmartLabel.width) { smartText = smartLabel.getSmartText(val, 2 * stop, effectivePlotArea.height); smartText.x = valueRatio[index] * effectivePlotArea.width / 100; leftStop = stop; labelHeights.push(smartText.height); } lsTexts[index] = smartText; } maxHeight = Math.max.apply(Math, labelHeights); effectivePlotArea.height = maxHeight; bBox.height = maxHeight + 2 * padding.v; lSpace.node.logicArea = effectivePlotArea; lSpace.bound = bBox; return bBox; }; LegendValues.prototype.draw = function () { var conf = this.conf, boundStyle = conf.bound && conf.bound.style || { stroke: 'none' }, keys, pos = {}, paper, layer, bound, legendValuesGroup, componentPool, bBox, lSpace, smartLabel, logicArea, options, cRange, valueRatio, index, length, smartTexts, smartText, instanceFn, numberFormatter; if (arguments.length >= 2) { bBox = arguments[0]; options = arguments[1]; } else if (arguments.length >= 1){ options = arguments[0]; } paper = options.paper; layer = options.parentLayer; smartLabel = options.smartLabel, cRange = options.colorRange; numberFormatter = options.numberFormatter, valueRatio = cRange.getCumulativeValueRatio(); componentPool = options.componentPool; keys = componentPool.getKeys(); this.getLogicalSpace(bBox, options); lSpace = this._lSpace; logicArea = lSpace.node.logicArea; smartTexts = lSpace.node.smartTexts; instanceFn = componentPool.getComponent(this._id, keys.KEY_GROUP); legendValuesGroup = instanceFn('legend-values', layer); instanceFn = componentPool.getComponent(this._id, keys.KEY_RECT); this.bound = bound = instanceFn(legendValuesGroup).attr(lSpace.bound).css(boundStyle); instanceFn = componentPool.getComponent(this._id, keys.KEY_TEXT, true); for (index = 0, length = valueRatio.length; index < length; index++) { smartText = smartTexts[index]; if (!smartText) { continue; } pos.y = logicArea.y + smartText.height / 2; if (index === length - 1) { pos.x = logicArea.x + smartText.x - smartText.width / 2; } else if (index){ pos.x = logicArea.x + smartText.x; } else { pos.x = logicArea.x + smartText.x + smartText.width / 2; } instanceFn({}, legendValuesGroup).attr({ text: smartText.text, x: pos.x, y: pos.y, lineHeight: this._metaStyle.lineHeight, fill: conf.style.fill }); } return { group: legendValuesGroup, bound: bound }; }; function VerticalLegendValues () { LegendValues.apply(this, arguments); this._id = 'GL_VALUES'; } VerticalLegendValues.prototype = Object.create(LegendValues.prototype); VerticalLegendValues.prototype.constructor = VerticalLegendValues; VerticalLegendValues.prototype.getLogicalSpace = function (bBox, options, recalculate) { var lSpace = this._lSpace, conf = this.conf, padding = conf.padding, componentPool = options.componentPool, chart = componentPool.getChart(), cRange, smartLabel, crDataObj, smartText, index, length, valueRatio, stop, nextRefPoint, currPoint, effectivePlotArea, zerothStop, labelWidths = [], topBound, topStop, maxWidth, bottomBound, val, copyOfStyle, testSmartLabel, lsTexts; if (lSpace && !recalculate) { lSpace.isImpure = true; // If logical space has already been calculated and recalculate flag is on, return without recalculating. return lSpace; } cRange = options.colorRange; smartLabel = options.smartLabel; crDataObj = cRange.colorRange; valueRatio = cRange.getCumulativeValueRatio(); lSpace = this._lSpace = { bound: { height: 0, width: 0 }, node: { logicArea: undefined, smartTexts: [] } }; lsTexts = lSpace.node.smartTexts; effectivePlotArea = merge(bBox, {}); // The logical rect inside which the composition are drawn effectivePlotArea.height -= 2 * padding.v; effectivePlotArea.width -= 2 * padding.h; effectivePlotArea.x += padding.h; effectivePlotArea.y += padding.v; smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow); copyOfStyle = merge(conf.style, {}); normalizeFontSizeAppend(copyOfStyle); smartLabel.setStyle(this._metaStyle = copyOfStyle); testSmartLabel = smartLabel.getSmartText('W'); length = crDataObj.length; zerothStop = stop = ((valueRatio[length - 1] - valueRatio[0]) / 2) * effectivePlotArea.height / 100; // first scale Label smartText = smartLabel.getSmartText(crDataObj[0].displayValue, effectivePlotArea.width, zerothStop); smartText.y = valueRatio[0] * effectivePlotArea.height / 100; topBound = smartText.y + smartText.width; labelWidths.push(smartText.width); lsTexts[0] = smartText; //last scale label smartText = smartLabel.getSmartText(crDataObj[length - 1].displayValue, effectivePlotArea.width, zerothStop); smartText.y = valueRatio[length - 1] * effectivePlotArea.height / 100; bottomBound = smartText.y - smartText.height; labelWidths.push(smartText.width); lsTexts[length - 1] = smartText; topStop = topBound; for (index = 1; index < length - 1; index++) { smartText = undefined; val = crDataObj[index].displayValue; nextRefPoint = index + 1 === length - 1 ? bottomBound : valueRatio[index + 1] * effectivePlotArea.height / 100; currPoint = valueRatio[index] * effectivePlotArea.height / 100; stop = Math.min(currPoint - topStop, nextRefPoint - currPoint); if (stop > 2 * testSmartLabel.height) { smartText = smartLabel.getSmartText(val, effectivePlotArea.width, 2 * stop); smartText.y = valueRatio[index] * effectivePlotArea.height / 100; topStop = stop; labelWidths.push(smartText.width); } lsTexts[index] = smartText; } maxWidth = Math.max.apply(Math, labelWidths); effectivePlotArea.width = maxWidth; bBox.width = maxWidth + 2 * padding.h; lSpace.node.logicArea = effectivePlotArea; lSpace.bound = bBox; return bBox; }; VerticalLegendValues.prototype.draw = function () { var paper, layer, bound, conf = this.conf, boundStyle = conf.bound && conf.bound.style || { stroke: 'none' }, legendValuesGroup, bBox, lSpace, smartLabel, logicArea, options, cRange, valueRatio, smartTexts, smartText, index, length, pos = {}, componentPool, instanceFn, keys; if (arguments.length >= 2) { bBox = arguments[0]; options = arguments[1]; } else if (arguments.length >= 1){ options = arguments[0]; } paper = options.paper; layer = options.parentLayer; smartLabel = options.smartLabel, cRange = options.colorRange; valueRatio = cRange.getCumulativeValueRatio(); componentPool = options.componentPool; keys = componentPool.getKeys(); this.getLogicalSpace(bBox, options); lSpace = this._lSpace; logicArea = lSpace.node.logicArea; smartTexts = lSpace.node.smartTexts; instanceFn = componentPool.getComponent(this._id, keys.KEY_GROUP); legendValuesGroup = instanceFn('legend-values', layer); instanceFn = componentPool.getComponent(this._id, keys.KEY_RECT); this.bound = bound = instanceFn(legendValuesGroup).attr(lSpace.bound).css(boundStyle); instanceFn = componentPool.getComponent(this._id, keys.KEY_TEXT, true); for (index = 0, length = valueRatio.length; index < length; index++) { smartText = smartTexts[index]; if (!smartText) { continue; } pos.x = logicArea.x + smartText.width / 2; if (index === length - 1) { pos.y = logicArea.y + smartText.y - smartText.height / 2; } else if (index){ pos.y = logicArea.y + smartText.y; } else { pos.y = logicArea.y + smartText.y + smartText.height / 2; } instanceFn({ }, legendValuesGroup).attr({ text: smartText.text, x: pos.x, y: pos.y, lineHeight: this._metaStyle.lineHeight, fill: conf.style.fill }); } return { group: legendValuesGroup, bound: bound }; }; function LegendAxis (conf) { this.conf = conf; this._id = 'FL_AXIS'; this.node = undefined; this.shadow = undefined; this.markerLine = undefined; this.compositionsByCategory = {}; } LegendAxis.prototype.constructor = LegendAxis; LegendAxis.prototype.addCompositions = function (instance, category) { this.compositionsByCategory[category] = instance; }; LegendAxis.prototype.getLogicalSpace = function (bBox, options, recalculate) { var lSpace = this._lSpace, conf = this.conf, padding = conf.padding, heightTakenLower, heightTakenUpper, heightTaken, axisThickness = conf.legendAxisHeight, compositionsByCategory = this.compositionsByCategory, sliderG, effectivePlotArea, slider, sliderSpace, sliderExtraDiam = 0; if (lSpace && !recalculate) { lSpace.isImpure = true; // If logical space has already been calculated and recalculate flag is on, return without recalculating. return lSpace; } lSpace = this._lSpace = { bound: { height: 0, width: 0 }, node: { logicArea: undefined } }; effectivePlotArea = merge(bBox, {}); // The logical rect inside which the composition are drawn effectivePlotArea.height -= 2 * padding.v; effectivePlotArea.width -= 2 * padding.h; effectivePlotArea.x += padding.h; effectivePlotArea.y += padding.v; heightTakenLower = (axisThickness / 2) + conf.line.offset; heightTakenUpper = axisThickness / 2; sliderG = compositionsByCategory[compositionKeys.RANGE]; if (sliderG) { slider = sliderG.sliders[false]; sliderSpace = slider.conf.outerCircle.rFactor * axisThickness; heightTakenUpper += sliderExtraDiam = Math.max(sliderSpace / 2 - axisThickness / 2, 0); } // @todo Change the height and width of bBox effectivePlotArea.y += sliderExtraDiam; effectivePlotArea.height = heightTaken = heightTakenUpper + heightTakenLower + sliderExtraDiam; bBox.height = heightTaken + 2 * padding.v; lSpace.node.logicArea = effectivePlotArea; lSpace.bound = bBox; return bBox; }; LegendAxis.prototype.getDrawableAxisArea = function (parentBoundingRect) { var conf = this.conf, x = parentBoundingRect.x, y = parentBoundingRect.y, width = parentBoundingRect.width, height = conf.legendAxisHeight, r = conf.legendAxisHeight / 2; return { x: x, y: y, width: width, height: height, r: r }; }; LegendAxis.prototype.preDrawingRangeParam = function (drawableArea) { var y = drawableArea.y + (drawableArea.height / 2), calculationBase = drawableArea.height; return { y: y, calculationBase: calculationBase, rangeStart: drawableArea.x, rangeEnd: drawableArea.x + drawableArea.width, prop: 'y' }; }; LegendAxis.prototype.getScaleMarkerPathStr = function (oriAxisRect, valueRatio) { var axisRect = merge(oriAxisRect, {}), conf = this.conf, lineAttr = conf.line, index, length, ratio, covered, markerStartY, tickStr = '', lineStr = ''; axisRect.x += axisRect.r; axisRect.width -= 2 * axisRect.r; markerStartY = axisRect.y + axisRect.height; for (index = 0, length = valueRatio.length; index < length; index++) { ratio = valueRatio[index]; covered = axisRect.x + (ratio * axisRect.width / 100); tickStr += M + (covered) + COMMA_STR + (markerStartY - lineAttr.grooveLength) + L + (covered) + COMMA_STR + (markerStartY + lineAttr.offset); } lineStr += M + (axisRect.x) + COMMA_STR + (markerStartY + lineAttr.offset) + L + (axisRect.x + axisRect.width) + COMMA_STR + (markerStartY + lineAttr.offset); return tickStr + lineStr; }; LegendAxis.prototype.getColorGradient = function (colorRange) { return { axis: colorRange.getBoxFill(), shadow: toRaphaelColor({ FCcolor: { alpha : '25,0,0', angle : 90, color : '000000,FFFFFF,FFFFFF', ratio : '0,30,40' } }) }; }; LegendAxis.prototype.draw = function () { var paper, layer, bound, conf = this.conf, boundAttr = conf.bound || {}, lineAttr = conf.line, boundStyle = boundAttr.style || {}, node, bBox, category, compositionsByCategory = this.compositionsByCategory, cRange, valueRatio, composition, compositionMes, legendAxisGroup, rangeParams, grad, oriAxisRect, lSpace, options, instanceFn, keys, componentPool, scaleLine; if (arguments.length >= 2) { bBox = arguments[0]; options = arguments[1]; } else if (arguments.length >= 1){ options = arguments[0]; } paper = options.paper; layer = options.parentLayer; cRange = options.colorRange; valueRatio = cRange.getCumulativeValueRatio(); componentPool = options.componentPool; keys = componentPool.getKeys(); this.getLogicalSpace(bBox, options); lSpace = this._lSpace; instanceFn = componentPool.getComponent(this._id, keys.KEY_GROUP); legendAxisGroup = instanceFn('legend-axis', layer); //legendAxisGroup = paper.group('legend-axis', layer); instanceFn = componentPool.getComponent(this._id, keys.KEY_RECT, true); this.bound = bound = instanceFn(legendAxisGroup).attr(lSpace.bound).css(boundStyle); //bound = this.bound = paper.rect(lSpace.bound, legendAxisGroup).css(boundStyle); oriAxisRect = this.getDrawableAxisArea(lSpace.node.logicArea); grad = this.getColorGradient(cRange); conf.style.fill = grad.axis; conf.shadow.style.fill = grad.shadow; // node = this.node = paper.rect(oriAxisRect, legendAxisGroup).css(conf.style); // this.shadow = paper.rect(oriAxisRect, legendAxisGroup).css(conf.shadow.style); node = this.node = instanceFn(legendAxisGroup).attr(oriAxisRect).css(conf.style); this.shadow = instanceFn(legendAxisGroup).attr(oriAxisRect).css(conf.shadow.style); scaleLine = this.getScaleMarkerPathStr(oriAxisRect, valueRatio); instanceFn = componentPool.getComponent(this._id, keys.KEY_PATH); instanceFn('M0,0', legendAxisGroup) .attr({ path: scaleLine }).css(lineAttr.style); for (category in compositionsByCategory) { composition = compositionsByCategory[category]; switch (category) { case compositionKeys.RANGE: rangeParams = this.preDrawingRangeParam(oriAxisRect); options[rangeParams.prop] = rangeParams[rangeParams.prop]; options.key = rangeParams.prop; options.rCalcBase = rangeParams.calculationBase; options.parentLayer = legendAxisGroup; compositionMes = composition.draw(rangeParams.rangeStart,rangeParams.rangeEnd, options); } } }; function VerticalLegendAxis () { LegendAxis.apply(this, arguments); } VerticalLegendAxis.prototype = Object.create(LegendAxis.prototype); VerticalLegendAxis.prototype.constructor = VerticalLegendAxis; VerticalLegendAxis.prototype.getLogicalSpace = function (bBox, options, recalculate) { var lSpace = this._lSpace, conf = this.conf, padding = conf.padding, widthTakenLower, widthTakenUpper, widthTaken, axisThickness = conf.legendAxisHeight, compositionsByCategory = this.compositionsByCategory, sliderG, slider, effectivePlotArea, sliderSpace, sliderExtraDiam = 0; if (lSpace && !recalculate) { lSpace.isImpure = true; // If logical space has already been calculated and recalculate flag is on, return without recalculating. return lSpace; } lSpace = this._lSpace = { bound: { height: 0, width: 0 }, node: { logicArea: undefined } }; effectivePlotArea = merge(bBox, {}); // The logical rect inside which the composition are drawn effectivePlotArea.height -= 2 * padding.v; effectivePlotArea.width -= 2 * padding.h; effectivePlotArea.x += padding.h; effectivePlotArea.y += padding.v; widthTakenLower = (axisThickness / 2) + conf.line.offset; widthTakenUpper = axisThickness / 2; sliderG = compositionsByCategory[compositionKeys.RANGE]; if (sliderG) { slider = sliderG.sliders[false]; sliderSpace = slider.conf.outerCircle.rFactor * axisThickness; widthTakenUpper += sliderExtraDiam = Math.max(sliderSpace / 2 - axisThickness / 2, 0); } effectivePlotArea.x += sliderExtraDiam; effectivePlotArea.width = widthTaken = widthTakenUpper + widthTakenLower + sliderExtraDiam; bBox.width = widthTaken + 2 * padding.v; lSpace.node.logicArea = effectivePlotArea; lSpace.bound = bBox; return bBox; }; VerticalLegendAxis.prototype.getDrawableAxisArea = function (parentBoundingRect) { var conf = this.conf; return { x: parentBoundingRect.x, y: parentBoundingRect.y, width: conf.legendAxisHeight, height: parentBoundingRect.height, r: conf.legendAxisHeight / 2 }; }; VerticalLegendAxis.prototype.getScaleMarkerPathStr = function (oriAxisRect, valueRatio) { var axisRect = merge(oriAxisRect, {}), conf = this.conf, lineAttr = conf.line, index, ratio, length, markerStartX, covered, tickStr = '', lineStr = ''; axisRect.y += axisRect.r; axisRect.height -= 2 * axisRect.r; markerStartX = axisRect.x + axisRect.width; for (index = 0, length = valueRatio.length; index < length; index++) { ratio = valueRatio[index]; covered = axisRect.y + (ratio * axisRect.height / 100); tickStr += M + (markerStartX - lineAttr.grooveLength) + COMMA_STR + (covered) + L + (markerStartX + lineAttr.offset) + COMMA_STR + (covered); } lineStr += M + (markerStartX + lineAttr.offset) + COMMA_STR + (axisRect.y) + L + (markerStartX + lineAttr.offset) + COMMA_STR + (axisRect.y + axisRect.height); return tickStr + lineStr; }; VerticalLegendAxis.prototype.getColorGradient = function (colorRange) { return { axis: colorRange.getBoxFill(true), shadow: toRaphaelColor({ FCcolor: { alpha : '25,0,0', angle : 360, color : '000000,FFFFFF,FFFFFF', ratio : '0,30,40' } }) }; }; VerticalLegendAxis.prototype.preDrawingRangeParam = function (drawableArea) { var x = drawableArea.x + (drawableArea.width / 2), calculationBase = drawableArea.width; return { x: x, calculationBase: calculationBase, rangeStart: drawableArea.y, rangeEnd: drawableArea.y + drawableArea.height, prop: 'x' }; }; function SliderGroup (conf) { var sliderConf = conf, options = {}; this._id = 'GL_SG1'; this.conf = conf; options.conf = sliderConf; this.extremes = []; this.sliders = {}; options.sliderGroup = this; this.valueRange = []; this.callbacks = []; this.sliders[FORMER_SLIDER_INDEX] = new Slider(FORMER_SLIDER_INDEX, options, this._id + '_' + (+FORMER_SLIDER_INDEX)); this.sliders[LATER_SLIDER_INDEX] = new Slider(LATER_SLIDER_INDEX, options, this._id + '_' + (+LATER_SLIDER_INDEX)); } SliderGroup.prototype.constructor = SliderGroup; SliderGroup.prototype.initRange = function (slider, updatedRange) { var sliderIndex = slider.sliderIndex; this.extremes[+sliderIndex] = updatedRange; }; SliderGroup.prototype.updateRange = function (slider, updatedRange) { var sliderIndex = slider.sliderIndex, sliders = this.sliders, s = sliders[!sliderIndex]; s.updateSwingRange(sliderIndex, updatedRange); }; SliderGroup.prototype.reset = function () { var options = {}; options.conf = this.conf; options.sliderGroup = this; // @todo do it from a more generalized way this.sliders[FORMER_SLIDER_INDEX] = new Slider(FORMER_SLIDER_INDEX, options, this._id + '_' + (+FORMER_SLIDER_INDEX)); this.sliders[LATER_SLIDER_INDEX] = new Slider(LATER_SLIDER_INDEX, options, this._id + '_' + (+LATER_SLIDER_INDEX)); this.draw.apply(this, this._drawParams); }; SliderGroup.prototype.clearListeners = function () { this.callbacks.length = 0; }; SliderGroup.prototype.draw = function (rangeStart, rangeEnd, options) { var sliders = this.sliders, lSlider = sliders[FORMER_SLIDER_INDEX], rSlider = sliders[LATER_SLIDER_INDEX], cRange = options.colorRange, colorRange = cRange.colorRange, componentPool = options.componentPool, chart = this._fcChart = componentPool.getChart(), oneSliderMes; this.getValueFormPixel = function (valueStart, valueEnd, pixelStart, pixelEnd) { var unit = (valueEnd - valueStart) / (pixelEnd - pixelStart); this.getValueFormPixel = function (pixel) { return valueStart + (unit * pixel); }; }; this.updateWhenInMove = function (numberFormatter, mapByPercent) { this.updateWhenInMove = function (slider, val) { var extremes = this.extremes, sliderIndex = slider.sliderIndex, nVal, value; if (sliderIndex) { nVal = extremes[1] - extremes[0] + val; } else { nVal = val; } value = this.getValueFormPixel(nVal); if (!mapByPercent) { value = numberFormatter.legendValue(value); } else { value = parseFloat(value).toFixed(2) + PERCENT_STR; } return value; }; }; this._drawParams = [rangeStart, rangeEnd, options]; this.updateWhenInMove(chart.components.numberFormatter, cRange.mapByPercent); oneSliderMes = lSlider.draw(rangeStart, colorRange[0].displayValue, options[options.key], options); oneSliderMes = rSlider.draw(rangeEnd, colorRange[colorRange.length - 1].displayValue, options[options.key], options); lSlider.swing = this.extremes.slice(0); rSlider.swing = this.extremes.slice(0); this.getValueFormPixel(colorRange[0].value, colorRange[colorRange.length - 1].value, this.extremes[0], this.extremes[1]); return oneSliderMes; }; SliderGroup.prototype.registerListener = function (fn, context, params) { this.callbacks.push({ fn: fn, context: context, params: params || [] }); }; SliderGroup.prototype.updateWhenInRest = function (slider, val) { var sliders = this.sliders, extremes = this.extremes, sliderIndex = slider.sliderIndex, lValue, rValue, cIndex, cLength, callbacks = this.callbacks, cb, params; if (sliderIndex) { lValue = sliders[!sliderIndex].currPos; rValue = extremes[1] - extremes[0] + val; } else { lValue = val; rValue = extremes[1] - extremes[0] + sliders[!sliderIndex].currPos; } for (cIndex = 0, cLength = callbacks.length; cIndex < cLength; cIndex++) { cb = callbacks[cIndex]; params = cb.params.slice(0); params.unshift(this.getValueFormPixel(rValue)); params.unshift(this.getValueFormPixel(lValue)); cb.fn.apply(cb.context, params); } }; SliderGroup.prototype.dragStarted = function (self) { var sliders = this.sliders, extremes = this.extremes, conf = self.conf, chart = this._fcChart; global.raiseEvent ('legendpointerdragstart', { pointerIndex: +self.sliderIndex, pointers: [{ value: this.getValueFormPixel(sliders[false].currPos) }, { value: this.getValueFormPixel(extremes[1] - extremes[0] + sliders[true].currPos) }], legendPointerHeight: conf.outerRadius, legendPointerWidth: conf.innerRadius, outerRadius: conf.outerRadius, innerRadius: conf.innerRadius }, chart.chartInstance, [chart.id]); }; SliderGroup.prototype.dragCompleted = function (self, isDragged, newVal) { var sliders = this.sliders, extremes = this.extremes, conf = self.conf, minValue = this.getValueFormPixel(sliders[false].currPos), maxValue = this.getValueFormPixel(extremes[1] - extremes[0] + sliders[true].currPos), chart = this._fcChart, newMinValue, newMaxValue; if (!self.sliderIndex) { newMinValue = this.getValueFormPixel(newVal); newMaxValue = maxValue; } else { newMinValue = minValue; newMaxValue = this.getValueFormPixel(extremes[1] - extremes[0] + newVal); } if (isDragged) { global.raiseEvent ('legendrangeupdated', { previousMinValue: minValue, previousMaxValue: maxValue, minValue: newMinValue, maxValue: newMaxValue }, chart.chartInstance, [chart.id]); } global.raiseEvent ('legendpointerdragstop', { pointerIndex: +self.sliderIndex, pointers: [{ value: minValue }, { value: maxValue }], legendPointerHeight: conf.outerRadius, legendPointerWidth: conf.innerRadius, outerRadius: conf.outerRadius, innerRadius: conf.innerRadius }, chart.chartInstance, [chart.id]); }; function Slider (sliderIndex, options, id) { this.conf = options.conf; this.sliderIndex = sliderIndex; this.rangeGroup = options.sliderGroup; this._id = id; this.node = undefined; this.tracker = undefined; this.currPos = 0; this.swing = []; } Slider.prototype.constructor = Slider; Slider.prototype.updateSwingRange = function (index, value) { this.swing[+index] = value; }; // @todo: Who should be responsible for reset, range or individual slider // Slider.prototype.reset = function () { // var node = this.node, // tracker = this.tracker, // conf = this.conf, // dragAPI; // if (!node) { // return; // } // node.attr({ // transform: 't0,0' // }); // this.currPos = 0; // this.swing = []; // }; Slider.prototype.draw = function (rangeStart, scaleVal, position, options) { var layer = options.parentLayer, conf = this.conf, ocConf = conf.outerCircle, icConf = conf.innerCircle, ocRadius = Math.ceil((ocConf.rFactor * options.rCalcBase ) / 2), icRadius = Math.ceil((icConf.rFactor * options.rCalcBase ) / 2), icThickness = ocRadius - icRadius, group, rangeGroup = this.rangeGroup, sliderIndex = this.sliderIndex, dragAPI, strokeWidthOffset, x, y, tracker, instanceFn, iniRange, componentPool = options.componentPool, keys = componentPool.getKeys(); conf.outerRadius = ocRadius; conf.innerRadius = icRadius; this._scaleVal = scaleVal; icConf.style['stroke-width'] = icThickness; strokeWidthOffset = Math.ceil(ocConf.style['stroke-width'] / 2); icRadius += strokeWidthOffset; instanceFn = componentPool.getComponent(this._id, keys.KEY_GROUP); group = this.node = instanceFn('fc-gl-slider', layer).attr({ 'cursor' : 'pointer', 'transform': 't0,0' }); if (options.key === 'x') { x = position; y = rangeStart; y += sliderIndex ? -(icRadius) : +icRadius; iniRange = y; } else { x = rangeStart; y = position; x += sliderIndex ? -(icRadius) : +icRadius; iniRange = x; } rangeGroup.initRange(this, iniRange); instanceFn = componentPool.getComponent(this._id, keys.KEY_CIRCLE, true); instanceFn(group).attr({ cx: x, cy: y, r: ocRadius }).css(ocConf.style); instanceFn(group).attr({ cx: x, cy: y, r: icRadius }).css(icConf.style); tracker = this.tracker = instanceFn(group).attr({ cx: x, cy: y, r: ocRadius + 5, ishot: true, fill: TRACKER_FILL, stroke: 0, cursor: 'pointer' }).trackTooltip(conf.showTooltip ? true : false).tooltip(scaleVal, null, null, true); this._dragAPI = dragAPI = this.getDragAPI(options.key === 'x'); tracker.undrag(); tracker.drag(dragAPI.dragging, dragAPI.dragStart, dragAPI.dragEnd); return { translateAscending: ocRadius + strokeWidthOffset }; }; Slider.prototype.getDragAPI = function (verticalDragging) { var self = this, node = self.node, index = self.sliderIndex, range = self.rangeGroup, swing, lastDisplacement, timeoutId, innerRadius = self.conf.innerRadius, spaceSaved = innerRadius, isDragged; return { dragging : function () { var left, right, d, event; event = arguments[4]; event.stopPropagation(); event.preventDefault(); if (verticalDragging) { d = arguments[1]; } else { d = arguments[0]; } if (index) { left = swing[0] - swing[1] + spaceSaved; right = 0; } else { left = 0; right = swing[1] - swing[0] - spaceSaved; } if ((self.currPos + d) < left || (self.currPos + d) > right) { return; } node.attr({ transform : verticalDragging ? 't0,' + (self.currPos + d) : 't' + (self.currPos + d) + ',' + 0 }); lastDisplacement = d; timeoutId && clearTimeout(timeoutId); timeoutId = setTimeout(function () { range.updateWhenInRest(self, self.currPos + d); }, 100); self.tracker.tooltip(range.updateWhenInMove(self, self.currPos + d), null, null, true); isDragged = true; return true; }, dragStart : function (x, y, event) { event.stopPropagation(); event.preventDefault(); node.attr({ transform : verticalDragging ? 't0,' + self.currPos : 't' + self.currPos + ',' + 0 }); swing = swing || self.swing; isDragged = false; range.dragStarted(self); }, dragEnd : function () { var newPos; range.dragCompleted(self, isDragged, self.currPos + lastDisplacement); if (!isDragged) { return; } timeoutId && clearTimeout(timeoutId); timeoutId = setTimeout(function () { range.updateWhenInRest(self, self.currPos); }, 100); self.currPos += lastDisplacement; newPos = swing[+index] + self.currPos; range.updateRange(self, newPos); } }; }; function ColorRange (data, options, chart) { var numberFormatter = chart.components.numberFormatter, index, length, range, min, max, minRange, maxRange, minValue, maxValue, mapByPercent; this.data = data; this.options = options || {}; mapByPercent = this.mapByPercent = !!data.mapByPercent; this.appender = ''; min = this.mapByPercent ? 0 : options.min; max = this.mapByPercent ? 100 : options.max; if (data.colorRange.length === 2) { minRange = data.colorRange[0]; maxRange = data.colorRange[1]; minValue = minRange.value = isInvalid(minRange.value) ? min : minRange.value; maxValue = maxRange.value = isInvalid(maxRange.value) ? max : maxRange.value; if (minValue === maxValue) { minValue = minRange.value = min; maxValue = maxRange.value = max; } minRange.displayValue = mapByPercent ? minValue + PERCENT_STR : numberFormatter.legendValue(minValue); maxRange.displayValue = mapByPercent ? maxValue + PERCENT_STR : numberFormatter.legendValue(maxValue); } if ((isInvalid(min) && isInvalid(minRange.value)) || (isInvalid(max) && isInvalid(minRange.value)) || !data.gradient) { this._preparationGoneWrong = true; } else { this._preparationGoneWrong = false; } range = this.colorRange = data.colorRange.sort(function (m, n) { return m.value - n.value; }); this.valueRatio = undefined; this.values = []; for (index = 0, length = range.length; index < length; index++) { this.values.push(range[index].value); } } ColorRange.prototype.constructor = ColorRange; ColorRange.prototype.getValueRatio = function () { var colorRange = this.colorRange, currentRange, index, length = colorRange.length, ratio = this.valueRatio, maxValue = colorRange[length - 1].value, minValue = colorRange[0].value, range = maxValue - minValue, itemValuePercent, lastValue = 0; if (ratio) { return ratio; } ratio = this.valueRatio = []; for (index = 0; index < length; index++) { currentRange = colorRange[index]; itemValuePercent = (currentRange.value - minValue) / range; ratio.push((itemValuePercent - lastValue) * 100); lastValue = itemValuePercent; } return ratio; }; ColorRange.prototype.getCumulativeValueRatio = function () { var colorRange = this.colorRange, currentRange, index, length = colorRange.length, firstValue = colorRange[0].value, lastValue = colorRange[length - 1].value, ratio = []; for (index = 0; index < length; index++) { currentRange = colorRange[index]; ratio.push((currentRange.value - firstValue) / (lastValue - firstValue) * 100); } return ratio; }; ColorRange.prototype.getBoxFill = function (isVertical) { var colorRange = this.colorRange, currentRange, index, length = colorRange.length, color = [], raphColorArg, angle; angle = isVertical ? 90 : 0; for (index = 0; index < length; index++) { currentRange = colorRange[index]; color.push(currentRange.code); } raphColorArg = { FCcolor: { alpha: '100,100,100', angle: angle, color: color.join(COMMA_STR), ratio: this.getValueRatio().join(COMMA_STR) } }; return toRaphaelColor(raphColorArg); }; ColorRange.prototype.getColorByValue = function (nVal) { var valueArr = this.values, colorRange = this.colorRange, length, index, rangeOutSideColor, color; if (nVal === undefined || nVal === null) { return; } for (index = 0, length = valueArr.length; index < length; index++) { if (nVal === valueArr[index]) { color = colorRange[index].code; break; } else if (!index && nVal < valueArr[index]) { rangeOutSideColor = true; break; } else if (index === length - 1 && nVal > valueArr[index]) { rangeOutSideColor = true; break; }else if (nVal > valueArr[index] && nVal < valueArr[index + 1]) { color = getColorBetween(colorRange[index], colorRange[index + 1], nVal); break; } } if (rangeOutSideColor) { return; } return color; }; function GradientLegend () { LegendBase.apply(this, arguments); } GradientLegend.prototype = Object.create(LegendBase.prototype); GradientLegend.prototype.constructor = GradientLegend; FusionCharts.register('component', ['gradientLegend', 'gradientLegend', { pIndex: 1, enabled: false, /* * Initializes the component. * Calling this would create instance of colorRange. Which can be used to access the api of the same. * Configure is called when the second level of init is called. The second level is explained below. * * * Example use * legend = new (FusionCharts.register('component',['gradientLegend', 'gradientLegend']))(); * * 1st level - fn = legend.init({chart: chart}); * 2nd level - fn({min: 100, max: 1000, maxOtherSide: 150}); * * Or putting it together - legend.init({chart: chart, dataExtremes: {min: 100, max: 1000, maxOtherSide: 150}}) * colorRange = legend.colorRange * * * @param options {Object} - A simple key value pair needed for init. * { * chart: chartInstance, * dataExtremes: optional data extreme of the datasets in the form * { * max: maximum value, * min: minimum value, * maxOtherSide: Optional maximum other side measurement. This takes the maximum allowed height for * a horizontal legend and width for vertical legend. * }, * } * * @return {Function | undefined} - If the dataExtremes are provided return undefined, else returns a function * where it is to be fed. */ init: function (options) { var componentAPI = this, iapi = options.chart, dataExtremes, cr, nData, fcChart; /* * Performs 2nd level of initialization. * Injects the colorRange in the API object. * * @param de {Object} - A simple key value pair needed for 2nd level of init. See the above dataExtremes obj */ function continueInit (de) { // Extracts raw FC Chart data and parse it to prepare data for colorRange. This kind of acts like a // adapter. componentAPI.data = options.chart.jsonData.colorrange; nData = componentAPI.nData = legendManager.legacyDataParser(componentAPI.data, de); if (!nData) { // If no valid data is present, sets a flag not to draw the legend . This will be read by subsequent // calls. componentAPI._dontPlot = true; return; } // Starts prepaeparing the drawOptions object which is needed during the final drawing and space // management. componentAPI.drawOptions = { smartLabel: iapi.linkedItems.smartLabel, colorRange: (componentAPI.colorRange = cr = new ColorRange(nData, de, fcChart)), maxOtherSide: de.maxOtherSide }; componentAPI._dontPlot = false; cr && cr._preparationGoneWrong && (componentAPI._dontPlot = true); componentAPI._recalculateLogicalSpace = true; // Parse all the legend releted attrs. componentAPI._configure(); } // Initiates the legend manager by providing the access to the chart object. legendManager.init(options); // Saves the reference of chart for internal use, in case fcChart = componentAPI._chart = options.chart; componentAPI._cpool = componentPoolFactory(fcChart); if (!(dataExtremes = options.dataExtremes)) { // If the dataExtremes is not provided, return the function for 2nd level of init return continueInit; } // If provided perform the 2nd level of init internally by giving the feel to the user that init happened // in one single shot continueInit(dataExtremes); }, /* * Parses all the FC attributes for gradient legend */ _configure: function () { var componentAPI = this, chart = componentAPI._chart, chartAttrs = chart.jsonData.chart, conf = componentAPI.conf = {}, outCanvasBaseFont = chartAttrs.outcnvbasefont, outCanvasBaseFontSize = chartAttrs.outcnvbasefontsize, outCanvasBaseFontColor = chartAttrs.outcnvbasefontcolor, labelStyle = chart.config.dataLabelStyle, fColor, fFamily, fSize, fWeight, cfColor, cfFamily, cfSize, cfWeight, axisBorderColor, axisBorderAlpha; conf.caption = pluck(chartAttrs.legendcaption); conf.legendPosition = pluck(chartAttrs.legendposition, 'bottom').toLowerCase(); conf.showLegend = pluckNumber(chartAttrs.showlegend, 1); conf.interactiveLegend = pluckNumber(chartAttrs.interactivelegend, 1); conf.showLegendLabels = pluckNumber(chartAttrs.showlegendlabels, 1); fColor = chartAttrs.legenditemfontcolor || outCanvasBaseFontColor; fFamily = chartAttrs.legenditemfont || outCanvasBaseFont; fSize = chartAttrs.legenditemfontsize || outCanvasBaseFontSize; fWeight = pluckNumber(chartAttrs.legenditemfontbold, 0); cfColor = chartAttrs.legendcaptionfontcolor || outCanvasBaseFontColor; cfFamily = chartAttrs.legendcaptionfont || outCanvasBaseFont; cfSize = chartAttrs.legendcaptionfontsize || outCanvasBaseFontSize; cfWeight = pluckNumber(chartAttrs.legendcaptionfontbold, 1); axisBorderColor = chartAttrs.legendaxisbordercolor ? hashify(dehashify(chartAttrs.legendaxisbordercolor)) : undefined; axisBorderAlpha = axisBorderColor ? pluckNumber(chartAttrs.legendaxisborderalpha, 100) / 100 : undefined; // We do this like this so that, it becomes easy to override the default configuartion. The default conf was // created in this format. conf.axisTextItemConf = { style: { fill: fColor ? convertColor(pluck(fColor)) : labelStyle.color, fontFamily: fFamily ? pluck(fFamily) : labelStyle.fontFamily, fontSize: fSize ? pluckNumber(fSize) : labelStyle.fontSize.match(/\d+/)[0], fontWeight: fWeight ? 'bold' : labelStyle.fontWeight } }; conf.legendCaptionConf = { style : { fill: cfColor ? convertColor(pluck(cfColor)) : labelStyle.color, fontFamily: cfFamily ? pluck(cfFamily) : labelStyle.fontFamily, fontSize: cfSize ? pluckNumber(cfSize) : labelStyle.fontSize.match(/\d+/)[0], fontWeight: cfWeight ? 'bold' : labelStyle.fontWeight, fontStyle: 'normal' } }; conf.legendAxisConf = { legendAxisHeight: 11, style : { stroke: axisBorderColor, 'stroke-opacity': axisBorderAlpha }, line: { style: { stroke: convertColor(pluck(chartAttrs.legendscalelinecolor, 'FFF8E9'), pluckNumber(chartAttrs.legendscalelinealpha, 100)), 'stroke-width': pluckNumber(chartAttrs.legendscalelinethickness) } } }; conf.sliderGroupConf = { showTooltip: pluckNumber(chartAttrs.showtooltip, 1), outerCircle : { rFactor: pluckNumber(chartAttrs.sliderdiameterfactor), style: { stroke: convertColor(pluck(chartAttrs.legendpointerbordercolor, '757575'), pluckNumber(chartAttrs.legendpointerborderalpha, 100)) } }, innerCircle : { rFactor: pluckNumber(chartAttrs.sliderholediameterfactor), style: { stroke: convertColor(pluck(chartAttrs.legendpointercolor, 'FFFFFF'), pluckNumber(chartAttrs.legendpointeralpha, 100)) } } }; conf.legendCarpetConf = { spreadFactor : pluckNumber(chartAttrs.legendspreadfactor), allowDrag: !!pluckNumber(chartAttrs.legendallowdrag, 0), captionAlignment: pluck(chartAttrs.legendcaptionalignment, 'center'), style : { 'fill' : convertColor(pluck(chartAttrs.legendbgcolor, 'e4d9c1'), pluckNumber(chartAttrs.legendbgalpha, 100)), 'stroke' : convertColor(pluck(chartAttrs.legendbordercolor, 'c4b89d'), pluckNumber(chartAttrs.legendborderalpha, 100)), 'stroke-width': pluckNumber(chartAttrs.legendborderthickness, 1) } }; }, /* * This takes care of the instance creation of various component based on attrs. Dependency injection / * component injection is managed from here. * This is not meant to be called from outside. This gets called internally. */ postConfigureInit : function () { var componentAPI = this, conf = componentAPI.conf, caption, carpet, axis, sGroup, gl, ovrdConf, ovrdTextConf, body, labels, values; // Container for the component instances. componentAPI.elem = {}; // The details of the various components and their relationship is written at the top. if (conf.caption) { // If caption is present, create a instance of the same ovrdConf = merge(legendManager.getDefaultConf('legendCaptionConf'), conf.legendCaptionConf); caption = new LegendCaption(conf.caption, ovrdConf); } if (conf.interactiveLegend) { // If interactiveLegend is attr is present, creates the sliders of the legend. // Override the default conf with the user given one. ovrdConf = merge(legendManager.getDefaultConf('sliderGroupConf'), conf.sliderGroupConf); componentAPI.elem.sGroup = sGroup = new SliderGroup(ovrdConf); componentAPI.listeners && componentAPI.listeners.length > 0 && sGroup.registerListener.apply(sGroup, componentAPI.listeners); } ovrdConf = merge(legendManager.getDefaultConf('legendCarpetConf'), conf.legendCarpetConf); if (conf.legendPosition === 'bottom') { // If the legend to be placed at bottom // The reference side would be width and will be covered the complete canvasWidth. // The max of other side is given by maxOtherSide. // The margin is taken from canvasLeft componentAPI.drawOptions.refSideKey = 'canvasWidth'; componentAPI.drawOptions.refOffsetKey = 'canvasLeft'; // Creates a horizontal capet, axis, labels and values component carpet = new LegendCarpet(ovrdConf); ovrdTextConf = merge(legendManager.getDefaultConf('axisTextItemConf'), conf.axisTextItemConf); body = new LegendBody(componentAPI.drawOptions.colorRange, legendManager.getDefaultConf('legendBodyConf'), ovrdTextConf); axis = new LegendAxis(merge(legendManager.getDefaultConf('legendAxisConf'), conf.legendAxisConf)); conf.showLegendLabels && (labels = new LegendLabels(ovrdTextConf)); values = new LegendValues(ovrdTextConf); } else { // If the legend to be placed at bottom // The reference side would be height and will be covered the complete canvasWidth. // The max of other side is given by maxOtherSide. // The margin is taken from canvasTop componentAPI.drawOptions.refSideKey = 'canvasHeight'; componentAPI.drawOptions.refOffsetKey = 'canvasTop'; ovrdTextConf = merge(legendManager.getDefaultConf('axisTextItemConf'), conf.axisTextItemConf); carpet = new VerticalLegendCarpet(ovrdConf); body = new VerticalLegendBody(componentAPI.drawOptions.colorRange, legendManager.getDefaultConf('legendBodyConf'), ovrdTextConf); axis = new VerticalLegendAxis(merge(legendManager.getDefaultConf('legendAxisConf'), conf.legendAxisConf)); conf.showLegendLabels && (labels = new VerticalLegendLabels(ovrdTextConf)); values = new VerticalLegendValues(ovrdTextConf); } // If the slider group is defined, inject as component. Slider group is component of axis. sGroup && axis.addCompositions(sGroup, compositionKeys.RANGE); // Adds component to the body if available labels && body.addCompositions(labels, compositionKeys.AXIS_LABEL); body.addCompositions(axis, compositionKeys.LEGEND_AXIS); body.addCompositions(values, compositionKeys.AXIS_VALUE); // Adds the body and the caption to the carpet caption && carpet.addCompositions(caption, compositionKeys.CAPTION); carpet.addCompositions(body, compositionKeys.LEGEND_BODY); // Finally creates the gradient legend. componentAPI.elem.gl = gl = new GradientLegend(carpet, componentAPI._cpool); }, notifyWhenUpdate: function (fn, context, params) { var componentAPI = this, rGroup; rGroup = componentAPI.elem && componentAPI.elem.sGroup; if (rGroup) { rGroup.registerListener(fn, context, params); } else { componentAPI.listeners = [fn, context, params]; } }, dispose: function () { var componentAPI = this; componentAPI.elem && componentAPI.elem.gl && componentAPI.elem.gl.dispose(); componentAPI.elem = {}; }, getLogicalSpace: function (maxOtherSide) { var componentAPI = this, conf = componentAPI.conf, zeroArea = { height: 0, width: 0 }, drawOptions = componentAPI.drawOptions, refSideKey, chart = componentAPI._chart, refOffsetKey; // @todo redundancy in calculating the reference side. Make it from a organized procedure call. if (!componentAPI._recalculateLogicalSpace) { refSideKey = drawOptions.refSideKey; refOffsetKey = drawOptions.refOffsetKey; componentAPI.drawOptions.refSide = chart.config[refSideKey]; componentAPI.drawOptions.refOffset = chart.config[refOffsetKey]; (componentAPI._logicalArea = componentAPI.elem.gl.getLogicalSpace(componentAPI.drawOptions, true)); return componentAPI._logicalArea || zeroArea; } if (componentAPI._dontPlot) { return zeroArea; } componentAPI._recalculateLogicalSpace = false; componentAPI.postConfigureInit(); if (!conf.showLegend) { return zeroArea; } refSideKey = drawOptions.refSideKey; refOffsetKey = drawOptions.refOffsetKey; componentAPI.drawOptions.refSide = chart.config[refSideKey]; componentAPI.drawOptions.refOffset = chart.config[refOffsetKey]; componentAPI.drawOptions.maxOtherSide = maxOtherSide || componentAPI.drawOptions.maxOtherSide; return componentAPI.elem.gl && (componentAPI._logicalArea = componentAPI.elem.gl.getLogicalSpace(componentAPI.drawOptions, true)); }, resetLegend: function() { var componentAPI = this, rGroup; rGroup = componentAPI.elem && componentAPI.elem.sGroup; if (rGroup) { rGroup.reset(); } }, clearListeners: function () { var componentAPI = this, rGroup; rGroup = componentAPI.elem && componentAPI.elem.sGroup; if (rGroup) { rGroup.clearListeners(); } }, draw: function (x, y, options) { var componentAPI = this, conf = componentAPI.conf, measurement, node; if (componentAPI._dontPlot) { return; } componentAPI._cpool.init(options.paper); if (!conf.showLegend) { componentAPI.enabled = false; return; } componentAPI.drawOptions.paper = options.paper; componentAPI.drawOptions.parentGroup = options.parentGroup; componentAPI.drawOptions.x = x; componentAPI.drawOptions.y = y; componentAPI.drawOptions.maxOtherSide = componentAPI.drawOptions.maxOtherSide || options.maxOtherSide; node = componentAPI.elem.gl.draw(componentAPI.drawOptions); measurement = node.getBBox(); conf.xPos = measurement.x; conf.yPos = measurement.y; conf.height = measurement.height; conf.width = measurement.width; componentAPI.enabled = true; } }]); }]); /**! * @license FusionCharts JavaScript Library * Copyright FusionCharts Technologies LLP * License Information at * * @version 3.12.0 */ /** * @private * @module fusioncharts.renderer.javascript.msstepline */ FusionCharts.register('module', ['private', 'modules.renderer.js-msstepline', function() { var global = this, lib = global.hcLib, win = global.window, creditLabel = false && !lib.CREDIT_REGEX.test(win.location.hostname), COMPONENT = 'component', DATASET = 'dataset', chartAPI = lib.chartAPI, pluckNumber = lib.pluckNumber, pluck = lib.pluck, Image = win.Image, preDefStr = lib.preDefStr, schedular = lib.schedular, LINE = preDefStr.line, toRaphaelColor = lib.toRaphaelColor, getFirstValue = lib.getFirstValue, configStr = preDefStr.configStr, animationObjStr = preDefStr.animationObjStr, dataLabelStr = preDefStr.dataLabelStr, BLANKSTRING = lib.BLANKSTRING, hiddenStr = preDefStr.hiddenStr, SETROLLOVERATTR = 'setRolloverAttr', SETROLLOUTATTR = 'setRolloutAttr', math = Math, mathMax = math.max, hasTouch = lib.hasTouch, TOUCH_THRESHOLD_PIXELS = lib.TOUCH_THRESHOLD_PIXELS, CLICK_THRESHOLD_PIXELS = lib.CLICK_THRESHOLD_PIXELS, HTP = hasTouch ? TOUCH_THRESHOLD_PIXELS : CLICK_THRESHOLD_PIXELS, M = 'M', H = 'H', V = 'V', ROUND = preDefStr.ROUND, miterStr = preDefStr.miterStr, UNDEFINED, NORMALSTRING = 'normal'; chartAPI('msstepline', { friendlyName: 'Multi-series Step Line Chart', standaloneInit: true, creditLabel: creditLabel, defaultDatasetType: 'msstepline', defaultPlotShadow: 1, applicableDSList: {'msstepline': true} }, chartAPI.mscartesian, { drawverticaljoins: 1, useforwardsteps: 1, zeroplanethickness: 1, zeroplanealpha: 40, showzeroplaneontop: 0, enablemousetracking: true }, chartAPI.areabase); FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-msstepline', function () { var COMPONENT = 'component', DATASET_GROUP = 'datasetGroup'; FusionCharts.register(COMPONENT, [DATASET_GROUP, 'msstepline', { },'line']); } ]); FusionCharts.register(COMPONENT, [DATASET, 'MSStepLine', { type: 'msstepline', _addLegend: function () { var dataset = this, chart = dataset.chart, conf = dataset.config, legend = chart.components.legend, drawAnchors = pluckNumber(conf.drawanchors, 1), config = { enabled: conf.includeinlegend, type : LINE, /* In case of scatter (a child chartAPI of line), line is drawn in legend only when drawLine is set to true. */ drawLine: pluck(conf.drawLine, true), fillColor : toRaphaelColor({ color: conf.anchorbgcolor, alpha: conf.anchorbgalpha }), strokeColor: toRaphaelColor({ color: conf.anchorbordercolor, alpha: '100' }), rawFillColor: conf.anchorbgcolor, rawStrokeColor: conf.anchorbordercolor, anchorSide: drawAnchors ? conf.anchorsides : 0, strokeWidth: conf.anchorborderthickness, label : getFirstValue(dataset.JSONData.seriesname), lineWidth: conf.linethickness }; dataset.legendItemId = legend.addItems(dataset, dataset.legendInteractivity, config); }, draw: function () {// retrive requitrd objects var dataSet = this, JSONData = dataSet.JSONData, chart = dataSet.chart, jobList = chart.getJobList(), chartComponents = chart.components, conf = dataSet.config, datasetIndex = dataSet.index || dataSet.positionIndex, chartConfig = chart.config, len, i, paper = chartComponents.paper, xAxis = chartComponents.xAxis[0], yAxis = dataSet.yAxis, xPos, yPos, layers = chart.graphics, dataLabelsLayer = layers.datalabelsGroup, toolText, label, setElement, hotElement, setLink, setValue, eventArgs, displayValue, dataStore = dataSet.components.data, dataObj, setRolloutAttr, setRolloverAttr, removeDataArr = dataSet.components.removeDataArr || [], removeDataArrLen = removeDataArr.length, lineThickness = conf.linethickness, container = dataSet.graphics.container, connectNullData = chartConfig.connectnulldata, group = layers.datasetGroup, hoverEffects, shadow = conf.shadow, anchorShadow, dataLabelContainer = dataSet.graphics.dataLabelContainer, anchorProps = {}, imgRef, symbol, config, animationObj = chart.get(configStr, animationObjStr), dummyAnimElem = animationObj.dummyObj, dummyAnimObj = animationObj.animObj, animationDuration = animationObj.duration, pool = dataSet.components.pool || [], showValue, pathAttr, EVENTARGS = 'eventArgs', /* Called when transpose animation is completed for hiding the dataset */ animCallBack = function() { if (dataSet.visible === false && (dataSet._conatinerHidden === false || dataSet._conatinerHidden=== undefined)) { container.lineGroup.hide(); container.lineShadowGroup.hide(); container.anchorShadowGroup.hide(); container.anchorGroup.hide(); dataLabelContainer && dataLabelContainer.hide(); dataSet._conatinerHidden = true; } }, /* Called when the initial animation compeletes for showing the dataset */ initAnimCallBack = function () { group.lineConnector.attr({ 'clip-rect': null }); // fix for ie11 group.lineConnector.node && group.lineConnector.node.removeAttribute('clip-path'); if (dataSet.visible !== false) { container.lineShadowGroup.show(); container.anchorShadowGroup.show(); container.anchorGroup.show(); dataLabelContainer && dataLabelContainer.show(); } // animCompleteFn(); }, animFlag = true, setTooltext, connector, dashStyle, yBase = yAxis.getAxisBase(), yBasePos = yAxis.yBasePos = yAxis.getAxisPosition(yBase), clipConf = chartComponents.canvas.config.clip, clipCanvasInit = clipConf['clip-canvas-init'].slice(0), clipCanvas = clipConf['clip-canvas'].slice(0), lineDashStyle = conf.lineDashStyle, lineColorObj = { color: conf.linecolor, alpha: conf.alpha }, setColor, setDashStyle, lineSegmentChange, colorObj, linePath = [], lscthash, lineDrawing = 0, mainLinePath = [], lastYPos = null, lastXPos, lastMoveCommand = [], initialAnimation = false, connectorShadow, MAX_MITER_LINEJOIN = 2, cosmeticAttrs, lineElement = dataSet.graphics.lineElement, visible = dataSet.visible, drawVerticalJoins = conf.drawverticaljoins, useForwardSteps = conf.useforwardsteps, labelElement, imageElement, polypath, animType = animationObj.animType, isNewElem, noOfImages = 0, catLabel, halfStep = chart.config.stepatmiddle ? xAxis.getPVR() * 0.5 : 0, radius, borderThickness, topAnchorPos, bottomAnchorPos, canvasTop = chartConfig.canvasTop, canvasBottom = chartConfig.canvasBottom; conf.imagesLoaded = 0; /* * Creating lineConnector group and appending it to dataset layer if not created * Lineconnector group has the anchorgroups of all datasets */ group.lineConnector = group.lineConnector || paper.group('line-connector', group); // Create dataset container if not created if (!container) { container = dataSet.graphics.container = { lineShadowGroup: paper.group('connector-shadow', group.lineConnector), anchorShadowGroup: paper.group('anchor-shadow', group.lineConnector), lineGroup: paper.group(LINE, group.lineConnector), anchorGroup: paper.group('anchors', group.lineConnector) }; if (!visible) { container.lineShadowGroup.hide(); container.anchorShadowGroup.hide(); container.lineGroup.hide(); container.anchorGroup.hide(); } } if (!dataStore) { dataStore = dataSet.components.data = []; } if (!dataLabelContainer) { dataLabelContainer = dataSet.graphics.dataLabelContainer = dataSet.graphics.dataLabelContainer || paper.group(dataLabelStr, dataLabelsLayer); if (!visible) { dataLabelContainer.hide(); } } if (visible) { container.lineShadowGroup.show(); container.anchorShadowGroup.show(); container.lineGroup.show(); container.anchorGroup.show(); dataLabelContainer.show(); } len = xAxis.getCategoryLen(); //create plot elements for (i=0; i