3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.chart.series.Line
17 * @extends Ext.chart.series.Cartesian
19 * Creates a Line Chart. A Line Chart is a useful visualization technique to display quantitative information for different
20 * categories or other real values (as opposed to the bar chart), that can show some progression (or regression) in the dataset.
21 * As with all other series, the Line Series must be appended in the *series* Chart array configuration. See the Chart
22 * documentation for more information. A typical configuration object for the line series could be:
24 * {@img Ext.chart.series.Line/Ext.chart.series.Line.png Ext.chart.series.Line chart series}
26 * var store = Ext.create('Ext.data.JsonStore', {
27 * fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
29 * {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
30 * {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
31 * {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
32 * {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
33 * {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
37 * Ext.create('Ext.chart.Chart', {
38 * renderTo: Ext.getBody(),
48 * renderer: Ext.util.Format.numberRenderer('0,0')
50 * title: 'Sample Values',
57 * title: 'Sample Metrics'
93 * In this configuration we're adding two series (or lines), one bound to the `data1`
94 * property of the store and the other to `data3`. The type for both configurations is
95 * `line`. The `xField` for both series is the same, the name propert of the store.
96 * Both line series share the same axis, the left axis. You can set particular marker
97 * configuration by adding properties onto the markerConfig object. Both series have
98 * an object as highlight so that markers animate smoothly to the properties in highlight
99 * when hovered. The second series has `fill=true` which means that the line will also
100 * have an area below it of the same color.
102 * **Note:** In the series definition remember to explicitly set the axis to bind the
103 * values of the line series to. This can be done by using the `axis` configuration property.
105 Ext.define('Ext.chart.series.Line', {
107 /* Begin Definitions */
109 extend: 'Ext.chart.series.Cartesian',
111 alternateClassName: ['Ext.chart.LineSeries', 'Ext.chart.LineChart'],
113 requires: ['Ext.chart.axis.Axis', 'Ext.chart.Shape', 'Ext.draw.Draw', 'Ext.fx.Anim'],
115 /* End Definitions */
119 alias: 'series.line',
123 * The position of the axis to bind the values to. Possible values are 'left', 'bottom', 'top' and 'right'.
124 * You must explicitly set this value to bind the values of the line series to the ones in the axis, otherwise a
125 * relative scale will be used.
129 * @cfg {Number} selectionTolerance
130 * The offset distance from the cursor position to the line series to trigger events (then used for highlighting series, etc).
132 selectionTolerance: 20,
135 * @cfg {Boolean} showMarkers
136 * Whether markers should be displayed at the data points along the line. If true,
137 * then the {@link #markerConfig} config item will determine the markers' styling.
142 * @cfg {Object} markerConfig
143 * The display style for the markers. Only used if {@link #showMarkers} is true.
144 * The markerConfig is a configuration object containing the same set of properties defined in
145 * the Sprite class. For example, if we were to set red circles as markers to the line series we could
160 * @cfg {Object} style
161 * An object containing styles for the visualization lines. These styles will override the theme styles.
162 * Some options contained within the style object will are described next.
167 * @cfg {Boolean/Number} smooth
168 * If set to `true` or a non-zero number, the line will be smoothed/rounded around its points; otherwise
169 * straight line segments will be drawn.
171 * A numeric value is interpreted as a divisor of the horizontal distance between consecutive points in
172 * the line; larger numbers result in sharper curves while smaller numbers result in smoother curves.
174 * If set to `true` then a default numeric value of 3 will be used. Defaults to `false`.
179 * @private Default numeric smoothing value to be used when {@link #smooth} = true.
181 defaultSmoothness: 3,
184 * @cfg {Boolean} fill
185 * If true, the area below the line will be filled in using the {@link #style.eefill} and
186 * {@link #style.opacity} config properties. Defaults to false.
190 constructor: function(config) {
191 this.callParent(arguments);
193 surface = me.chart.surface,
194 shadow = me.chart.shadow,
196 Ext.apply(me, config, {
202 "stroke-opacity": 0.05,
203 stroke: 'rgb(0, 0, 0)',
210 "stroke-opacity": 0.1,
211 stroke: 'rgb(0, 0, 0)',
218 "stroke-opacity": 0.15,
219 stroke: 'rgb(0, 0, 0)',
226 me.group = surface.getGroup(me.seriesId);
227 if (me.showMarkers) {
228 me.markerGroup = surface.getGroup(me.seriesId + '-markers');
231 for (i = 0, l = this.shadowAttributes.length; i < l; i++) {
232 me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
237 // @private makes an average of points when there are more data points than pixels to be rendered.
238 shrink: function(xValues, yValues, size) {
239 // Start at the 2nd point...
240 var len = xValues.length,
241 ratio = Math.floor(len / size),
248 for (; i < len; ++i) {
249 xSum += xValues[i] || 0;
250 ySum += yValues[i] || 0;
251 if (i % ratio == 0) {
252 xRes.push(xSum/ratio);
253 yRes.push(ySum/ratio);
265 * Draws the series for the current chart.
267 drawSeries: function() {
270 store = chart.substore || chart.store,
271 surface = chart.surface,
272 chartBBox = chart.chartBBox,
275 gutterX = chart.maxGutter[0],
276 gutterY = chart.maxGutter[1],
277 showMarkers = me.showMarkers,
278 markerGroup = me.markerGroup,
279 enableShadows = chart.shadow,
280 shadowGroups = me.shadowGroups,
281 shadowAttributes = me.shadowAttributes,
283 lnsh = shadowGroups.length,
286 markerIndex = chart.markerIndex,
287 axes = [].concat(me.axis),
296 markerStyle = me.markerStyle,
297 seriesStyle = me.seriesStyle,
298 seriesLabelStyle = me.seriesLabelStyle,
299 colorArrayStyle = me.colorArrayStyle,
300 colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
307 isNumber = Ext.isNumber,
308 seriesIdx = me.seriesIdx, shadows, shadow, shindex, fromPath, fill, fillPath, rendererAttributes,
309 x, y, prevX, prevY, firstY, markerCount, i, j, ln, axis, ends, marker, markerAux, item, xValue,
310 yValue, coords, xScale, yScale, minX, maxX, minY, maxY, line, animation, endMarkerStyle,
311 endLineStyle, type, props, firstMarker, count, smoothPath, renderPath;
313 //if store is empty then there's nothing to draw.
314 if (!store || !store.getCount()) {
318 //prepare style objects for line and markers
319 endMarkerStyle = Ext.apply(markerStyle, me.markerConfig);
320 type = endMarkerStyle.type;
321 delete endMarkerStyle.type;
322 endLineStyle = Ext.apply(seriesStyle, me.style);
323 //if no stroke with is specified force it to 0.5 because this is
324 //about making *lines*
325 if (!endLineStyle['stroke-width']) {
326 endLineStyle['stroke-width'] = 0.5;
328 //If we're using a time axis and we need to translate the points,
329 //then reuse the first markers as the last markers.
330 if (markerIndex && markerGroup && markerGroup.getCount()) {
331 for (i = 0; i < markerIndex; i++) {
332 marker = markerGroup.getAt(i);
333 markerGroup.remove(marker);
334 markerGroup.add(marker);
335 markerAux = markerGroup.getAt(markerGroup.getCount() - 2);
336 marker.setAttributes({
340 x: markerAux.attr.translation.x,
341 y: markerAux.attr.translation.y
347 me.unHighlightItem();
348 me.cleanHighlights();
353 me.clipRect = [bbox.x, bbox.y, bbox.width, bbox.height];
355 chart.axes.each(function(axis) {
356 //only apply position calculations to axes that affect this series
357 //this means the axis in the position referred by this series and also
358 //the axis in the other coordinate for this series. For example: (left, top|bottom),
359 //or (top, left|right), etc.
360 if (axis.position == me.axis || axis.position != posHash[me.axis]) {
362 if (axis.type != 'Numeric') {
366 numericAxis = (numericAxis && axis.type == 'Numeric');
368 ends = axis.calcEnds();
369 if (axis.position == 'top' || axis.position == 'bottom') {
381 //If there's only one axis specified for a series, then we set the default type of the other
382 //axis to a category axis. So in this case numericAxis, which would be true if both axes affecting
383 //the series are numeric should be false.
384 if (numericAxis && axisCount == 1) {
388 // If a field was specified without a corresponding axis, create one to get bounds
389 //only do this for the axis where real values are bound (that's why we check for
391 if (me.xField && !isNumber(minX)) {
392 if (me.axis == 'bottom' || me.axis == 'top') {
393 axis = Ext.create('Ext.chart.axis.Axis', {
395 fields: [].concat(me.xField)
399 } else if (numericAxis) {
400 axis = Ext.create('Ext.chart.axis.Axis', {
402 fields: [].concat(me.xField),
410 if (me.yField && !isNumber(minY)) {
411 if (me.axis == 'right' || me.axis == 'left') {
412 axis = Ext.create('Ext.chart.axis.Axis', {
414 fields: [].concat(me.yField)
418 } else if (numericAxis) {
419 axis = Ext.create('Ext.chart.axis.Axis', {
421 fields: [].concat(me.yField),
431 xScale = bbox.width / (store.getCount() - 1);
434 //In case some person decides to set an axis' minimum and maximum
435 //configuration properties to the same value, then fallback the
436 //denominator to a > 0 value.
437 xScale = bbox.width / ((maxX - minX) || (store.getCount() - 1));
442 yScale = bbox.height / (store.getCount() - 1);
445 //In case some person decides to set an axis' minimum and maximum
446 //configuration properties to the same value, then fallback the
447 //denominator to a > 0 value.
448 yScale = bbox.height / ((maxY - minY) || (store.getCount() - 1));
451 store.each(function(record, i) {
452 xValue = record.get(me.xField);
453 yValue = record.get(me.yField);
454 //skip undefined values
455 if (typeof yValue == 'undefined' || (typeof yValue == 'string' && !yValue)) {
457 if (Ext.isDefined(Ext.global.console)) {
458 Ext.global.console.warn("[Ext.chart.series.Line] Skipping a store element with an undefined value at ", record, xValue, yValue);
464 if (typeof xValue == 'string' || typeof xValue == 'object'
465 //set as uniform distribution if the axis is a category axis.
466 || (me.axis != 'top' && me.axis != 'bottom' && !numericAxis)) {
469 if (typeof yValue == 'string' || typeof yValue == 'object'
470 //set as uniform distribution if the axis is a category axis.
471 || (me.axis != 'left' && me.axis != 'right' && !numericAxis)) {
474 storeIndices.push(i);
475 xValues.push(xValue);
476 yValues.push(yValue);
480 if (ln > bbox.width) {
481 coords = me.shrink(xValues, yValues, bbox.width);
490 for (i = 0; i < ln; i++) {
493 if (yValue === false) {
494 if (path.length == 1) {
498 me.items.push(false);
501 x = (bbox.x + (xValue - minX) * xScale).toFixed(2);
502 y = ((bbox.y + bbox.height) - (yValue - minY) * yScale).toFixed(2);
507 path = path.concat([x, y]);
509 if ((typeof firstY == 'undefined') && (typeof y != 'undefined')) {
512 // If this is the first line, create a dummypath to animate in from.
513 if (!me.line || chart.resizing) {
514 dummyPath = dummyPath.concat([x, bbox.y + bbox.height / 2]);
517 // When resizing, reset before animating
518 if (chart.animate && chart.resizing && me.line) {
519 me.line.setAttributes({
523 me.fillPath.setAttributes({
528 if (me.line.shadows) {
529 shadows = me.line.shadows;
530 for (j = 0, lnsh = shadows.length; j < lnsh; j++) {
532 shadow.setAttributes({
539 marker = markerGroup.getAt(count++);
541 marker = Ext.chart.Shape[type](surface, Ext.apply({
542 group: [group, markerGroup],
546 y: prevY || (bbox.y + bbox.height / 2)
548 value: '"' + xValue + ', ' + yValue + '"'
557 marker.setAttributes({
558 value: '"' + xValue + ', ' + yValue + '"',
572 value: [xValue, yValue],
575 storeItem: store.getAt(storeIndices[i])
581 if (path.length <= 1) {
582 //nothing to be rendered
587 smoothPath = Ext.draw.Draw.smooth(path, isNumber(smooth) ? smooth : me.defaultSmoothness);
590 renderPath = smooth ? smoothPath : path;
592 //Correct path if we're animating timeAxis intervals
593 if (chart.markerIndex && me.previousPath) {
594 fromPath = me.previousPath;
596 Ext.Array.erase(fromPath, 1, 2);
602 // Only create a line if one doesn't exist.
604 me.line = surface.add(Ext.apply({
608 stroke: endLineStyle.stroke || endLineStyle.fill
609 }, endLineStyle || {}));
610 //unset fill here (there's always a default fill withing the themes).
611 me.line.setAttributes({
614 if (!endLineStyle.stroke && colorArrayLength) {
615 me.line.setAttributes({
616 stroke: colorArrayStyle[seriesIdx % colorArrayLength]
621 shadows = me.line.shadows = [];
622 for (shindex = 0; shindex < lnsh; shindex++) {
623 shadowBarAttr = shadowAttributes[shindex];
624 shadowBarAttr = Ext.apply({}, shadowBarAttr, { path: dummyPath });
625 shadow = chart.surface.add(Ext.apply({}, {
627 group: shadowGroups[shindex]
629 shadows.push(shadow);
634 fillPath = renderPath.concat([
635 ["L", x, bbox.y + bbox.height],
636 ["L", bbox.x, bbox.y + bbox.height],
637 ["L", bbox.x, firstY]
640 me.fillPath = surface.add({
643 opacity: endLineStyle.opacity || 0.3,
644 fill: endLineStyle.fill || colorArrayStyle[seriesIdx % colorArrayLength],
649 markerCount = showMarkers && markerGroup.getCount();
653 //Add renderer to line. There is not unique record associated with this.
654 rendererAttributes = me.renderer(line, false, { path: renderPath }, i, store);
655 Ext.apply(rendererAttributes, endLineStyle || {}, {
656 stroke: endLineStyle.stroke || endLineStyle.fill
658 //fill should not be used here but when drawing the special fill path object
659 delete rendererAttributes.fill;
660 if (chart.markerIndex && me.previousPath) {
661 me.animation = animation = me.onAnimate(line, {
662 to: rendererAttributes,
668 me.animation = animation = me.onAnimate(line, {
669 to: rendererAttributes
674 shadows = line.shadows;
675 for(j = 0; j < lnsh; j++) {
676 if (chart.markerIndex && me.previousPath) {
677 me.onAnimate(shadows[j], {
678 to: { path: renderPath },
679 from: { path: fromPath }
682 me.onAnimate(shadows[j], {
683 to: { path: renderPath }
690 me.onAnimate(me.fillPath, {
693 fill: endLineStyle.fill || colorArrayStyle[seriesIdx % colorArrayLength]
694 }, endLineStyle || {})
700 for(i = 0; i < ln; i++) {
702 item = markerGroup.getAt(count++);
704 rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
706 to: Ext.apply(rendererAttributes, endMarkerStyle || {})
711 for(; count < markerCount; count++) {
712 item = markerGroup.getAt(count);
717 rendererAttributes = me.renderer(me.line, false, { path: renderPath, hidden: false }, i, store);
718 Ext.apply(rendererAttributes, endLineStyle || {}, {
719 stroke: endLineStyle.stroke || endLineStyle.fill
721 //fill should not be used here but when drawing the special fill path object
722 delete rendererAttributes.fill;
723 me.line.setAttributes(rendererAttributes, true);
724 //set path for shadows
726 shadows = me.line.shadows;
727 for(j = 0; j < lnsh; j++) {
728 shadows[j].setAttributes({
734 me.fillPath.setAttributes({
740 for(i = 0; i < ln; i++) {
742 item = markerGroup.getAt(count++);
744 rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
745 item.setAttributes(Ext.apply(endMarkerStyle || {}, rendererAttributes || {}), true);
749 for(; count < markerCount; count++) {
750 item = markerGroup.getAt(count);
756 if (chart.markerIndex) {
758 Ext.Array.erase(path, 1, 2);
760 Ext.Array.splice(path, 1, 0, path[1], path[2]);
762 me.previousPath = path;
768 // @private called when a label is to be created.
769 onCreateLabel: function(storeItem, item, i, display) {
771 group = me.labelsGroup,
774 endLabelStyle = Ext.apply(config, me.seriesLabelStyle);
776 return me.chart.surface.add(Ext.apply({
778 'text-anchor': 'middle',
781 'y': bbox.y + bbox.height / 2
782 }, endLabelStyle || {}));
785 // @private called when a label is to be created.
786 onPlaceLabel: function(label, storeItem, item, i, display, animate) {
789 resizing = chart.resizing,
791 format = config.renderer,
792 field = config.field,
796 radius = item.sprite.attr.radius,
799 label.setAttributes({
800 text: format(storeItem.get(field)),
804 if (display == 'rotate') {
805 label.setAttributes({
806 'text-anchor': 'start',
813 //correct label position to fit into the box
814 bb = label.getBBox();
817 x = x < bbox.x? bbox.x : x;
818 x = (x + width > bbox.x + bbox.width)? (x - (x + width - bbox.x - bbox.width)) : x;
819 y = (y - height < bbox.y)? bbox.y + height : y;
821 } else if (display == 'under' || display == 'over') {
822 //TODO(nicolas): find out why width/height values in circle bounding boxes are undefined.
823 bb = item.sprite.getBBox();
824 bb.width = bb.width || (radius * 2);
825 bb.height = bb.height || (radius * 2);
826 y = y + (display == 'over'? -bb.height : bb.height);
827 //correct label position to fit into the box
828 bb = label.getBBox();
830 height = bb.height/2;
831 x = x - width < bbox.x? bbox.x + width : x;
832 x = (x + width > bbox.x + bbox.width) ? (x - (x + width - bbox.x - bbox.width)) : x;
833 y = y - height < bbox.y? bbox.y + height : y;
834 y = (y + height > bbox.y + bbox.height) ? (y - (y + height - bbox.y - bbox.height)) : y;
837 if (me.chart.animate && !me.chart.resizing) {
839 me.onAnimate(label, {
846 label.setAttributes({
851 me.animation.on('afteranimate', function() {
860 //@private Overriding highlights.js highlightItem method.
861 highlightItem: function() {
863 me.callParent(arguments);
864 if (this.line && !this.highlighted) {
865 if (!('__strokeWidth' in this.line)) {
866 this.line.__strokeWidth = this.line.attr['stroke-width'] || 0;
868 if (this.line.__anim) {
869 this.line.__anim.paused = true;
871 this.line.__anim = Ext.create('Ext.fx.Anim', {
874 'stroke-width': this.line.__strokeWidth + 3
877 this.highlighted = true;
881 //@private Overriding highlights.js unHighlightItem method.
882 unHighlightItem: function() {
884 me.callParent(arguments);
885 if (this.line && this.highlighted) {
886 this.line.__anim = Ext.create('Ext.fx.Anim', {
889 'stroke-width': this.line.__strokeWidth
892 this.highlighted = false;
896 //@private called when a callout needs to be placed.
897 onPlaceCallout : function(callout, storeItem, item, i, display, animate, index) {
904 surface = chart.surface,
905 resizing = chart.resizing,
906 config = me.callouts,
908 prev = i == 0? false : items[i -1].point,
909 next = (i == items.length -1)? false : items[i +1].point,
910 cur = [+item.point[0], +item.point[1]],
911 dir, norm, normal, a, aprev, anext,
912 offsetFromViz = config.offsetFromViz || 30,
913 offsetToSide = config.offsetToSide || 10,
914 offsetBox = config.offsetBox || 3,
915 boxx, boxy, boxw, boxh,
916 p, clipRect = me.clipRect,
918 width: config.styles.width || 10,
919 height: config.styles.height || 10
923 //get the right two points
930 a = (next[1] - prev[1]) / (next[0] - prev[0]);
931 aprev = (cur[1] - prev[1]) / (cur[0] - prev[0]);
932 anext = (next[1] - cur[1]) / (next[0] - cur[0]);
934 norm = Math.sqrt(1 + a * a);
935 dir = [1 / norm, a / norm];
936 normal = [-dir[1], dir[0]];
938 //keep the label always on the outer part of the "elbow"
939 if (aprev > 0 && anext < 0 && normal[1] < 0
940 || aprev < 0 && anext > 0 && normal[1] > 0) {
943 } else if (Math.abs(aprev) < Math.abs(anext) && normal[0] < 0
944 || Math.abs(aprev) > Math.abs(anext) && normal[0] > 0) {
949 x = cur[0] + normal[0] * offsetFromViz;
950 y = cur[1] + normal[1] * offsetFromViz;
952 //box position and dimensions
953 boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
954 boxy = y - bbox.height /2 - offsetBox;
955 boxw = bbox.width + 2 * offsetBox;
956 boxh = bbox.height + 2 * offsetBox;
958 //now check if we're out of bounds and invert the normal vector correspondingly
959 //this may add new overlaps between labels (but labels won't be out of bounds).
960 if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) {
963 if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) {
968 x = cur[0] + normal[0] * offsetFromViz;
969 y = cur[1] + normal[1] * offsetFromViz;
971 //update box position and dimensions
972 boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
973 boxy = y - bbox.height /2 - offsetBox;
974 boxw = bbox.width + 2 * offsetBox;
975 boxh = bbox.height + 2 * offsetBox;
978 //set the line from the middle of the pie to the box.
979 me.onAnimate(callout.lines, {
981 path: ["M", cur[0], cur[1], "L", x, y, "Z"]
984 //set component position
986 callout.panel.setPosition(boxx, boxy, true);
990 //set the line from the middle of the pie to the box.
991 callout.lines.setAttributes({
992 path: ["M", cur[0], cur[1], "L", x, y, "Z"]
994 //set component position
996 callout.panel.setPosition(boxx, boxy);
1000 callout[p].show(true);
1004 isItemInPoint: function(x, y, item, i) {
1007 tolerance = me.selectionTolerance,
1020 dist1, dist2, dist, midx, midy,
1021 sqrt = Math.sqrt, abs = Math.abs;
1023 nextItem = items[i];
1024 prevItem = i && items[i - 1];
1027 prevItem = items[ln - 1];
1029 prevPoint = prevItem && prevItem.point;
1030 nextPoint = nextItem && nextItem.point;
1031 x1 = prevItem ? prevPoint[0] : nextPoint[0] - tolerance;
1032 y1 = prevItem ? prevPoint[1] : nextPoint[1];
1033 x2 = nextItem ? nextPoint[0] : prevPoint[0] + tolerance;
1034 y2 = nextItem ? nextPoint[1] : prevPoint[1];
1035 dist1 = sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
1036 dist2 = sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
1037 dist = Math.min(dist1, dist2);
1039 if (dist <= tolerance) {
1040 return dist == dist1? prevItem : nextItem;
1045 // @private toggle visibility of all series elements (markers, sprites).
1046 toggleAll: function(show) {
1048 i, ln, shadow, shadows;
1050 Ext.chart.series.Line.superclass.hideAll.call(me);
1053 Ext.chart.series.Line.superclass.showAll.call(me);
1056 me.line.setAttributes({
1060 if (me.line.shadows) {
1061 for (i = 0, shadows = me.line.shadows, ln = shadows.length; i < ln; i++) {
1062 shadow = shadows[i];
1063 shadow.setAttributes({
1070 me.fillPath.setAttributes({
1076 // @private hide all series elements (markers, sprites).
1077 hideAll: function() {
1078 this.toggleAll(false);
1081 // @private hide all series elements (markers, sprites).
1082 showAll: function() {
1083 this.toggleAll(true);