4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-chart-series-Line'>/**
19 </span> * @class Ext.chart.series.Line
20 * @extends Ext.chart.series.Cartesian
22 * Creates a Line Chart. A Line Chart is a useful visualization technique to display quantitative information for different
23 * categories or other real values (as opposed to the bar chart), that can show some progression (or regression) in the dataset.
24 * As with all other series, the Line Series must be appended in the *series* Chart array configuration. See the Chart
25 * documentation for more information. A typical configuration object for the line series could be:
28 * var store = Ext.create('Ext.data.JsonStore', {
29 * fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
31 * { 'name': 'metric one', 'data1': 10, 'data2': 12, 'data3': 14, 'data4': 8, 'data5': 13 },
32 * { 'name': 'metric two', 'data1': 7, 'data2': 8, 'data3': 16, 'data4': 10, 'data5': 3 },
33 * { 'name': 'metric three', 'data1': 5, 'data2': 2, 'data3': 14, 'data4': 12, 'data5': 7 },
34 * { 'name': 'metric four', 'data1': 2, 'data2': 14, 'data3': 6, 'data4': 1, 'data5': 23 },
35 * { 'name': 'metric five', 'data1': 4, 'data2': 4, 'data3': 36, 'data4': 13, 'data5': 33 }
39 * Ext.create('Ext.chart.Chart', {
40 * renderTo: Ext.getBody(),
49 * fields: ['data1', 'data2'],
51 * renderer: Ext.util.Format.numberRenderer('0,0')
53 * title: 'Sample Values',
61 * title: 'Sample Metrics'
101 * In this configuration we're adding two series (or lines), one bound to the `data1`
102 * property of the store and the other to `data3`. The type for both configurations is
103 * `line`. The `xField` for both series is the same, the name propert of the store.
104 * Both line series share the same axis, the left axis. You can set particular marker
105 * configuration by adding properties onto the markerConfig object. Both series have
106 * an object as highlight so that markers animate smoothly to the properties in highlight
107 * when hovered. The second series has `fill=true` which means that the line will also
108 * have an area below it of the same color.
110 * **Note:** In the series definition remember to explicitly set the axis to bind the
111 * values of the line series to. This can be done by using the `axis` configuration property.
113 Ext.define('Ext.chart.series.Line', {
115 /* Begin Definitions */
117 extend: 'Ext.chart.series.Cartesian',
119 alternateClassName: ['Ext.chart.LineSeries', 'Ext.chart.LineChart'],
121 requires: ['Ext.chart.axis.Axis', 'Ext.chart.Shape', 'Ext.draw.Draw', 'Ext.fx.Anim'],
123 /* End Definitions */
127 alias: 'series.line',
129 <span id='Ext-chart-series-Line-cfg-axis'> /**
130 </span> * @cfg {String} axis
131 * The position of the axis to bind the values to. Possible values are 'left', 'bottom', 'top' and 'right'.
132 * You must explicitly set this value to bind the values of the line series to the ones in the axis, otherwise a
133 * relative scale will be used.
136 <span id='Ext-chart-series-Line-cfg-selectionTolerance'> /**
137 </span> * @cfg {Number} selectionTolerance
138 * The offset distance from the cursor position to the line series to trigger events (then used for highlighting series, etc).
140 selectionTolerance: 20,
142 <span id='Ext-chart-series-Line-cfg-showMarkers'> /**
143 </span> * @cfg {Boolean} showMarkers
144 * Whether markers should be displayed at the data points along the line. If true,
145 * then the {@link #markerConfig} config item will determine the markers' styling.
149 <span id='Ext-chart-series-Line-cfg-markerConfig'> /**
150 </span> * @cfg {Object} markerConfig
151 * The display style for the markers. Only used if {@link #showMarkers} is true.
152 * The markerConfig is a configuration object containing the same set of properties defined in
153 * the Sprite class. For example, if we were to set red circles as markers to the line series we could
156 <pre><code>
162 </code></pre>
167 <span id='Ext-chart-series-Line-cfg-style'> /**
168 </span> * @cfg {Object} style
169 * An object containing style properties for the visualization lines and fill.
170 * These styles will override the theme styles. The following are valid style properties:
172 * - `stroke` - an rgb or hex color string for the background color of the line
173 * - `stroke-width` - the width of the stroke (integer)
174 * - `fill` - the background fill color string (hex or rgb), only works if {@link #fill} is `true`
175 * - `opacity` - the opacity of the line and the fill color (decimal)
181 * 'stroke-width': 10,
188 <span id='Ext-chart-series-Line-cfg-smooth'> /**
189 </span> * @cfg {Boolean/Number} smooth
190 * If set to `true` or a non-zero number, the line will be smoothed/rounded around its points; otherwise
191 * straight line segments will be drawn.
193 * A numeric value is interpreted as a divisor of the horizontal distance between consecutive points in
194 * the line; larger numbers result in sharper curves while smaller numbers result in smoother curves.
196 * If set to `true` then a default numeric value of 3 will be used. Defaults to `false`.
200 <span id='Ext-chart-series-Line-property-defaultSmoothness'> /**
201 </span> * @private Default numeric smoothing value to be used when {@link #smooth} = true.
203 defaultSmoothness: 3,
205 <span id='Ext-chart-series-Line-cfg-fill'> /**
206 </span> * @cfg {Boolean} fill
207 * If true, the area below the line will be filled in using the {@link #style eefill} and
208 * {@link #style opacity} config properties. Defaults to false.
212 constructor: function(config) {
213 this.callParent(arguments);
215 surface = me.chart.surface,
216 shadow = me.chart.shadow,
218 Ext.apply(me, config, {
223 "stroke-width": 6,
224 "stroke-opacity": 0.05,
225 stroke: 'rgb(0, 0, 0)',
231 "stroke-width": 4,
232 "stroke-opacity": 0.1,
233 stroke: 'rgb(0, 0, 0)',
239 "stroke-width": 2,
240 "stroke-opacity": 0.15,
241 stroke: 'rgb(0, 0, 0)',
248 me.group = surface.getGroup(me.seriesId);
249 if (me.showMarkers) {
250 me.markerGroup = surface.getGroup(me.seriesId + '-markers');
253 for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
254 me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
259 // @private makes an average of points when there are more data points than pixels to be rendered.
260 shrink: function(xValues, yValues, size) {
261 // Start at the 2nd point...
262 var len = xValues.length,
263 ratio = Math.floor(len / size),
270 for (; i < len; ++i) {
271 xSum += xValues[i] || 0;
272 ySum += yValues[i] || 0;
273 if (i % ratio == 0) {
274 xRes.push(xSum/ratio);
275 yRes.push(ySum/ratio);
286 <span id='Ext-chart-series-Line-method-drawSeries'> /**
287 </span> * Draws the series for the current chart.
289 drawSeries: function() {
292 chartAxes = chart.axes,
293 store = chart.getChartStore(),
294 storeCount = store.getCount(),
295 surface = me.chart.surface,
298 showMarkers = me.showMarkers,
299 markerGroup = me.markerGroup,
300 enableShadows = chart.shadow,
301 shadowGroups = me.shadowGroups,
302 shadowAttributes = me.shadowAttributes,
304 lnsh = shadowGroups.length,
305 dummyPath = ["M"],
306 path = ["M"],
307 renderPath = ["M"],
308 smoothPath = ["M"],
309 markerIndex = chart.markerIndex,
310 axes = [].concat(me.axis),
318 markerStyle = me.markerStyle,
319 seriesStyle = me.style,
320 colorArrayStyle = me.colorArrayStyle,
321 colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
322 isNumber = Ext.isNumber,
323 seriesIdx = me.seriesIdx,
324 boundAxes = me.getAxesForXAndYFields(),
325 boundXAxis = boundAxes.xAxis,
326 boundYAxis = boundAxes.yAxis,
327 shadows, shadow, shindex, fromPath, fill, fillPath, rendererAttributes,
328 x, y, prevX, prevY, firstX, firstY, markerCount, i, j, ln, axis, ends, marker, markerAux, item, xValue,
329 yValue, coords, xScale, yScale, minX, maxX, minY, maxY, line, animation, endMarkerStyle,
330 endLineStyle, type, count, items;
332 if (me.fireEvent('beforedraw', me) === false) {
336 //if store is empty or the series is excluded in the legend then there's nothing to draw.
337 if (!storeCount || me.seriesIsHidden) {
340 for (i = 0, ln = items.length; i < ln; ++i) {
341 if (items[i].sprite) {
342 items[i].sprite.hide(true);
349 //prepare style objects for line and markers
350 endMarkerStyle = Ext.apply(markerStyle || {}, me.markerConfig);
351 type = endMarkerStyle.type;
352 delete endMarkerStyle.type;
353 endLineStyle = seriesStyle;
354 //if no stroke with is specified force it to 0.5 because this is
355 //about making *lines*
356 if (!endLineStyle['stroke-width']) {
357 endLineStyle['stroke-width'] = 0.5;
359 //If we're using a time axis and we need to translate the points,
360 //then reuse the first markers as the last markers.
361 if (markerIndex && markerGroup && markerGroup.getCount()) {
362 for (i = 0; i < markerIndex; i++) {
363 marker = markerGroup.getAt(i);
364 markerGroup.remove(marker);
365 markerGroup.add(marker);
366 markerAux = markerGroup.getAt(markerGroup.getCount() - 2);
367 marker.setAttributes({
371 x: markerAux.attr.translation.x,
372 y: markerAux.attr.translation.y
378 me.unHighlightItem();
379 me.cleanHighlights();
383 me.clipRect = [bbox.x, bbox.y, bbox.width, bbox.height];
384 for (i = 0, ln = axes.length; i < ln; i++) {
385 axis = chartAxes.get(axes[i]);
387 ends = axis.calcEnds();
388 if (axis.position == 'top' || axis.position == 'bottom') {
398 // If a field was specified without a corresponding axis, create one to get bounds
399 //only do this for the axis where real values are bound (that's why we check for
401 if (me.xField && !isNumber(minX) &&
402 (boundXAxis == 'bottom' || boundXAxis == 'top') &&
403 !chartAxes.get(boundXAxis)) {
404 axis = Ext.create('Ext.chart.axis.Axis', {
406 fields: [].concat(me.xField)
411 if (me.yField && !isNumber(minY) &&
412 (boundYAxis == 'right' || boundYAxis == 'left') &&
413 !chartAxes.get(boundYAxis)) {
414 axis = Ext.create('Ext.chart.axis.Axis', {
416 fields: [].concat(me.yField)
423 xScale = bbox.width / ((storeCount - 1) || 1);
426 xScale = bbox.width / ((maxX - minX) || (storeCount -1) || 1);
431 yScale = bbox.height / ((storeCount - 1) || 1);
434 yScale = bbox.height / ((maxY - minY) || (storeCount - 1) || 1);
437 // Extract all x and y values from the store
438 me.eachRecord(function(record, i) {
439 xValue = record.get(me.xField);
442 if (typeof xValue == 'string' || typeof xValue == 'object' && !Ext.isDate(xValue)
443 //set as uniform distribution if the axis is a category axis.
444 || boundXAxis && chartAxes.get(boundXAxis) && chartAxes.get(boundXAxis).type == 'Category') {
445 if (xValue in xValueMap) {
446 xValue = xValueMap[xValue];
448 xValue = xValueMap[xValue] = i;
452 // Filter out values that don't fit within the pan/zoom buffer area
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 yValue == 'string' || typeof yValue == 'object' && !Ext.isDate(yValue)
465 //set as uniform distribution if the axis is a category axis.
466 || boundYAxis && chartAxes.get(boundYAxis) && chartAxes.get(boundYAxis).type == 'Category') {
469 storeIndices.push(i);
470 xValues.push(xValue);
471 yValues.push(yValue);
475 if (ln > bbox.width) {
476 coords = me.shrink(xValues, yValues, bbox.width);
485 for (i = 0; i < ln; i++) {
488 if (yValue === false) {
489 if (path.length == 1) {
493 me.items.push(false);
496 x = (bbox.x + (xValue - minX) * xScale).toFixed(2);
497 y = ((bbox.y + bbox.height) - (yValue - minY) * yScale).toFixed(2);
502 path = path.concat([x, y]);
504 if ((typeof firstY == 'undefined') && (typeof y != 'undefined')) {
508 // If this is the first line, create a dummypath to animate in from.
509 if (!me.line || chart.resizing) {
510 dummyPath = dummyPath.concat([x, bbox.y + bbox.height / 2]);
513 // When resizing, reset before animating
514 if (chart.animate && chart.resizing && me.line) {
515 me.line.setAttributes({
519 me.fillPath.setAttributes({
524 if (me.line.shadows) {
525 shadows = me.line.shadows;
526 for (j = 0, lnsh = shadows.length; j < lnsh; j++) {
528 shadow.setAttributes({
535 marker = markerGroup.getAt(count++);
537 marker = Ext.chart.Shape[type](surface, Ext.apply({
538 group: [group, markerGroup],
542 y: prevY || (bbox.y + bbox.height / 2)
544 value: '"' + xValue + ', ' + yValue + '"',
554 marker.setAttributes({
555 value: '"' + xValue + ', ' + yValue + '"',
569 value: [xValue, yValue],
572 storeItem: store.getAt(storeIndices[i])
578 if (path.length <= 1) {
579 //nothing to be rendered
584 smoothPath = Ext.draw.Draw.smooth(path, isNumber(smooth) ? smooth : me.defaultSmoothness);
587 renderPath = smooth ? smoothPath : path;
589 //Correct path if we're animating timeAxis intervals
590 if (chart.markerIndex && me.previousPath) {
591 fromPath = me.previousPath;
593 Ext.Array.erase(fromPath, 1, 2);
599 // Only create a line if one doesn't exist.
601 me.line = surface.add(Ext.apply({
605 stroke: endLineStyle.stroke || endLineStyle.fill
606 }, endLineStyle || {}));
609 me.line.setAttributes(Ext.apply({}, me.shadowOptions), true);
612 //unset fill here (there's always a default fill withing the themes).
613 me.line.setAttributes({
617 if (!endLineStyle.stroke && colorArrayLength) {
618 me.line.setAttributes({
619 stroke: colorArrayStyle[seriesIdx % colorArrayLength]
624 shadows = me.line.shadows = [];
625 for (shindex = 0; shindex < lnsh; shindex++) {
626 shadowBarAttr = shadowAttributes[shindex];
627 shadowBarAttr = Ext.apply({}, shadowBarAttr, { path: dummyPath });
628 shadow = surface.add(Ext.apply({}, {
630 group: shadowGroups[shindex]
632 shadows.push(shadow);
637 fillPath = renderPath.concat([
638 ["L", x, bbox.y + bbox.height],
639 ["L", firstX, bbox.y + bbox.height],
640 ["L", firstX, firstY]
643 me.fillPath = surface.add({
646 opacity: endLineStyle.opacity || 0.3,
647 fill: endLineStyle.fill || colorArrayStyle[seriesIdx % colorArrayLength],
652 markerCount = showMarkers && markerGroup.getCount();
656 //Add renderer to line. There is not unique record associated with this.
657 rendererAttributes = me.renderer(line, false, { path: renderPath }, i, store);
658 Ext.apply(rendererAttributes, endLineStyle || {}, {
659 stroke: endLineStyle.stroke || endLineStyle.fill
661 //fill should not be used here but when drawing the special fill path object
662 delete rendererAttributes.fill;
664 if (chart.markerIndex && me.previousPath) {
665 me.animation = animation = me.onAnimate(line, {
666 to: rendererAttributes,
672 me.animation = animation = me.onAnimate(line, {
673 to: rendererAttributes
678 shadows = line.shadows;
679 for(j = 0; j < lnsh; j++) {
680 shadows[j].show(true);
681 if (chart.markerIndex && me.previousPath) {
682 me.onAnimate(shadows[j], {
683 to: { path: renderPath },
684 from: { path: fromPath }
687 me.onAnimate(shadows[j], {
688 to: { path: renderPath }
695 me.fillPath.show(true);
696 me.onAnimate(me.fillPath, {
699 fill: endLineStyle.fill || colorArrayStyle[seriesIdx % colorArrayLength],
701 }, endLineStyle || {})
707 for(i = 0; i < ln; i++) {
709 item = markerGroup.getAt(count++);
711 rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
713 to: Ext.apply(rendererAttributes, endMarkerStyle || {})
719 for(; count < markerCount; count++) {
720 item = markerGroup.getAt(count);
723 // for(i = 0; i < (chart.markerIndex || 0)-1; i++) {
724 // item = markerGroup.getAt(i);
729 rendererAttributes = me.renderer(me.line, false, { path: renderPath, hidden: false }, i, store);
730 Ext.apply(rendererAttributes, endLineStyle || {}, {
731 stroke: endLineStyle.stroke || endLineStyle.fill
733 //fill should not be used here but when drawing the special fill path object
734 delete rendererAttributes.fill;
735 me.line.setAttributes(rendererAttributes, true);
736 //set path for shadows
738 shadows = me.line.shadows;
739 for(j = 0; j < lnsh; j++) {
740 shadows[j].setAttributes({
747 me.fillPath.setAttributes({
754 for(i = 0; i < ln; i++) {
756 item = markerGroup.getAt(count++);
758 rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
759 item.setAttributes(Ext.apply(endMarkerStyle || {}, rendererAttributes || {}), true);
764 for(; count < markerCount; count++) {
765 item = markerGroup.getAt(count);
771 if (chart.markerIndex) {
773 Ext.Array.erase(path, 1, 2);
775 Ext.Array.splice(path, 1, 0, path[1], path[2]);
777 me.previousPath = path;
782 me.fireEvent('draw', me);
785 // @private called when a label is to be created.
786 onCreateLabel: function(storeItem, item, i, display) {
788 group = me.labelsGroup,
791 endLabelStyle = Ext.apply(config, me.seriesLabelStyle);
793 return me.chart.surface.add(Ext.apply({
795 'text-anchor': 'middle',
798 'y': bbox.y + bbox.height / 2
799 }, endLabelStyle || {}));
802 // @private called when a label is to be created.
803 onPlaceLabel: function(label, storeItem, item, i, display, animate) {
806 resizing = chart.resizing,
808 format = config.renderer,
809 field = config.field,
813 radius = item.sprite.attr.radius,
816 label.setAttributes({
817 text: format(storeItem.get(field)),
821 if (display == 'rotate') {
822 label.setAttributes({
823 'text-anchor': 'start',
830 //correct label position to fit into the box
831 bb = label.getBBox();
834 x = x < bbox.x? bbox.x : x;
835 x = (x + width > bbox.x + bbox.width)? (x - (x + width - bbox.x - bbox.width)) : x;
836 y = (y - height < bbox.y)? bbox.y + height : y;
838 } else if (display == 'under' || display == 'over') {
839 //TODO(nicolas): find out why width/height values in circle bounding boxes are undefined.
840 bb = item.sprite.getBBox();
841 bb.width = bb.width || (radius * 2);
842 bb.height = bb.height || (radius * 2);
843 y = y + (display == 'over'? -bb.height : bb.height);
844 //correct label position to fit into the box
845 bb = label.getBBox();
847 height = bb.height/2;
848 x = x - width < bbox.x? bbox.x + width : x;
849 x = (x + width > bbox.x + bbox.width) ? (x - (x + width - bbox.x - bbox.width)) : x;
850 y = y - height < bbox.y? bbox.y + height : y;
851 y = (y + height > bbox.y + bbox.height) ? (y - (y + height - bbox.y - bbox.height)) : y;
854 if (me.chart.animate && !me.chart.resizing) {
856 me.onAnimate(label, {
863 label.setAttributes({
867 if (resizing && me.animation) {
868 me.animation.on('afteranimate', function() {
877 //@private Overriding highlights.js highlightItem method.
878 highlightItem: function() {
880 me.callParent(arguments);
881 if (me.line && !me.highlighted) {
882 if (!('__strokeWidth' in me.line)) {
883 me.line.__strokeWidth = me.line.attr['stroke-width'] || 0;
885 if (me.line.__anim) {
886 me.line.__anim.paused = true;
888 me.line.__anim = Ext.create('Ext.fx.Anim', {
891 'stroke-width': me.line.__strokeWidth + 3
894 me.highlighted = true;
898 //@private Overriding highlights.js unHighlightItem method.
899 unHighlightItem: function() {
901 me.callParent(arguments);
902 if (me.line && me.highlighted) {
903 me.line.__anim = Ext.create('Ext.fx.Anim', {
906 'stroke-width': me.line.__strokeWidth
909 me.highlighted = false;
913 //@private called when a callout needs to be placed.
914 onPlaceCallout : function(callout, storeItem, item, i, display, animate, index) {
921 surface = chart.surface,
922 resizing = chart.resizing,
923 config = me.callouts,
925 prev = i == 0? false : items[i -1].point,
926 next = (i == items.length -1)? false : items[i +1].point,
927 cur = [+item.point[0], +item.point[1]],
928 dir, norm, normal, a, aprev, anext,
929 offsetFromViz = config.offsetFromViz || 30,
930 offsetToSide = config.offsetToSide || 10,
931 offsetBox = config.offsetBox || 3,
932 boxx, boxy, boxw, boxh,
933 p, clipRect = me.clipRect,
935 width: config.styles.width || 10,
936 height: config.styles.height || 10
940 //get the right two points
947 a = (next[1] - prev[1]) / (next[0] - prev[0]);
948 aprev = (cur[1] - prev[1]) / (cur[0] - prev[0]);
949 anext = (next[1] - cur[1]) / (next[0] - cur[0]);
951 norm = Math.sqrt(1 + a * a);
952 dir = [1 / norm, a / norm];
953 normal = [-dir[1], dir[0]];
955 //keep the label always on the outer part of the "elbow"
956 if (aprev > 0 && anext < 0 && normal[1] < 0
957 || aprev < 0 && anext > 0 && normal[1] > 0) {
960 } else if (Math.abs(aprev) < Math.abs(anext) && normal[0] < 0
961 || Math.abs(aprev) > Math.abs(anext) && normal[0] > 0) {
966 x = cur[0] + normal[0] * offsetFromViz;
967 y = cur[1] + normal[1] * offsetFromViz;
969 //box position and dimensions
970 boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
971 boxy = y - bbox.height /2 - offsetBox;
972 boxw = bbox.width + 2 * offsetBox;
973 boxh = bbox.height + 2 * offsetBox;
975 //now check if we're out of bounds and invert the normal vector correspondingly
976 //this may add new overlaps between labels (but labels won't be out of bounds).
977 if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) {
980 if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) {
985 x = cur[0] + normal[0] * offsetFromViz;
986 y = cur[1] + normal[1] * offsetFromViz;
988 //update box position and dimensions
989 boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
990 boxy = y - bbox.height /2 - offsetBox;
991 boxw = bbox.width + 2 * offsetBox;
992 boxh = bbox.height + 2 * offsetBox;
995 //set the line from the middle of the pie to the box.
996 me.onAnimate(callout.lines, {
998 path: ["M", cur[0], cur[1], "L", x, y, "Z"]
1001 //set component position
1002 if (callout.panel) {
1003 callout.panel.setPosition(boxx, boxy, true);
1007 //set the line from the middle of the pie to the box.
1008 callout.lines.setAttributes({
1009 path: ["M", cur[0], cur[1], "L", x, y, "Z"]
1011 //set component position
1012 if (callout.panel) {
1013 callout.panel.setPosition(boxx, boxy);
1016 for (p in callout) {
1017 callout[p].show(true);
1021 isItemInPoint: function(x, y, item, i) {
1024 tolerance = me.selectionTolerance,
1037 dist1, dist2, dist, midx, midy,
1038 sqrt = Math.sqrt, abs = Math.abs;
1040 nextItem = items[i];
1041 prevItem = i && items[i - 1];
1044 prevItem = items[ln - 1];
1046 prevPoint = prevItem && prevItem.point;
1047 nextPoint = nextItem && nextItem.point;
1048 x1 = prevItem ? prevPoint[0] : nextPoint[0] - tolerance;
1049 y1 = prevItem ? prevPoint[1] : nextPoint[1];
1050 x2 = nextItem ? nextPoint[0] : prevPoint[0] + tolerance;
1051 y2 = nextItem ? nextPoint[1] : prevPoint[1];
1052 dist1 = sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
1053 dist2 = sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
1054 dist = Math.min(dist1, dist2);
1056 if (dist <= tolerance) {
1057 return dist == dist1? prevItem : nextItem;
1062 // @private toggle visibility of all series elements (markers, sprites).
1063 toggleAll: function(show) {
1065 i, ln, shadow, shadows;
1067 Ext.chart.series.Cartesian.prototype.hideAll.call(me);
1070 Ext.chart.series.Cartesian.prototype.showAll.call(me);
1073 me.line.setAttributes({
1077 if (me.line.shadows) {
1078 for (i = 0, shadows = me.line.shadows, ln = shadows.length; i < ln; i++) {
1079 shadow = shadows[i];
1080 shadow.setAttributes({
1087 me.fillPath.setAttributes({
1093 // @private hide all series elements (markers, sprites).
1094 hideAll: function() {
1095 this.toggleAll(false);
1098 // @private hide all series elements (markers, sprites).
1099 showAll: function() {
1100 this.toggleAll(true);