1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2009 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package mx.charts
13{
14
15import flash.display.DisplayObject;
16import flash.filters.DropShadowFilter;
17import flash.system.ApplicationDomain;
18
19import mx.charts.chartClasses.CartesianChart;
20import mx.charts.chartClasses.CartesianTransform;
21import mx.charts.chartClasses.DataTip;
22import mx.charts.chartClasses.DataTransform;
23import mx.charts.chartClasses.IChartElement;
24import mx.charts.chartClasses.IStackable;
25import mx.charts.chartClasses.NumericAxis;
26import mx.charts.chartClasses.Series;
27import mx.charts.series.ColumnSeries;
28import mx.charts.series.ColumnSet;
29import mx.charts.series.items.ColumnSeriesItem;
30import mx.charts.styles.HaloDefaults;
31import mx.core.IFlexModuleFactory;
32import mx.core.mx_internal;
33import mx.graphics.SolidColor;
34import mx.graphics.SolidColorStroke;
35import mx.graphics.Stroke;
36import mx.styles.CSSStyleDeclaration;
37
38use namespace mx_internal;
39
40//--------------------------------------
41//  Styles
42//--------------------------------------
43
44/**
45 *  Specifies a ratio of wide to draw the columns relative to
46 *  the category width, as a percentage in the range of 0 to 1.
47 *  A value of 1 uses the entire space, while a value of 0.6
48 *  uses 60% of the column's available space.
49 *  The actual column width used is the smaller of the
50 *  <code>columnWidthRatio</code> property and the
51 *  <code>maxColumnWidth</code> property.
52 *  Clustered columns divide this space proportionally
53 *  among the columns in each cluster.
54 *  The default value is 0.65.
55 *
56 *  @langversion 3.0
57 *  @playerversion Flash 9
58 *  @playerversion AIR 1.1
59 *  @productversion Flex 3
60 */
61[Style(name="columnWidthRatio", type="Number", inherit="no")]
62
63/**
64 *  Specifies how wide to draw the columns, in pixels.
65 *  The actual column width used is the smaller of this property
66 *  and the <code>columnWidthRatio</code> property.
67 *  Clustered columns divide this space proportionally
68 *  among the columns in each cluster.
69 *
70 *  @langversion 3.0
71 *  @playerversion Flash 9
72 *  @playerversion AIR 1.1
73 *  @productversion Flex 3
74 */
75[Style(name="maxColumnWidth", type="Number", format="Length", inherit="no")]
76
77/**
78 *  The class that is used by this component to render labels.
79 *
80 *  <p>It can be set to either the mx.controls.Label class
81 *  or the spark.components.Label class.</p>
82 *
83 *  @default spark.components.Label
84 *
85 *  @langversion 3.0
86 *  @playerversion Flash 10.2
87 *  @playerversion AIR 2.0
88 *  @productversion Flex 4
89 */
90[Style(name="labelClass", type="Class", inherit="no")]
91
92//--------------------------------------
93//  Other metadata
94//--------------------------------------
95
96[DefaultBindingProperty(destination="dataProvider")]
97
98[DefaultTriggerEvent("itemClick")]
99
100[IconFile("ColumnChart.png")]
101
102/**
103 *  The ColumnChart control represents data as a series of vertical columns
104 *  whose height is determined by values in the data.
105 *  You can use the ColumnChart to represent a variety of different charts
106 *  including simple columns, clustered columns, stacked,
107 *  100% stacked, and high/low.
108 *
109 *  <p>A ColumnChart control expects its <code>series</code> property
110 *  to contain an array of ColumnSeries objects.</p>
111 *
112 *  <p>Stacked and 100% column charts override the <code>minField</code>
113 *  property of their ColumnSeries objects.</p>
114 *
115 *  @mxml
116 *
117 *  <p>The <code>&lt;mx:ColumnChart&gt;</code> tag inherits all the properties
118 *  of its parent classes and adds the following properties:</p>
119 *
120 *  <pre>
121 *  &lt;mx:ColumnChart
122 *    <strong>Properties</strong>
123 *    extendLabelToEnd="false|true"
124 *    maxLabelWidth="<i>50</i>"
125 *    showLabelVertically="false|true"
126 *    type="<i>clustered|overlaid|stacked|100%</i>"
127 *
128 *    <strong>Styles</strong>
129 *    columnWidthRatio=".65"
130 *    maxColumnWidth="<i>No default</i>"
131 *  /&gt;
132 *  </pre>
133 *
134 *  @includeExample examples/Column_BarChartExample.mxml
135 *
136 *  @see mx.charts.series.ColumnSeries
137 *
138 *  @langversion 3.0
139 *  @playerversion Flash 9
140 *  @playerversion AIR 1.1
141 *  @productversion Flex 3
142 */
143public class ColumnChart extends CartesianChart
144{
145    include "../core/Version.as";
146
147    //--------------------------------------------------------------------------
148    //
149    //  Class initialization
150    //
151    //--------------------------------------------------------------------------
152
153    //--------------------------------------------------------------------------
154    //
155    //  Class constants
156    //
157    //--------------------------------------------------------------------------
158
159    /**
160     *  @private
161     */
162    private static var INVALIDATING_STYLES:Object =
163    {
164        columnWidthRatio: 1,
165        maxColumnWidth: 1
166    }
167
168    //--------------------------------------------------------------------------
169    //
170    //  Constructor
171    //
172    //--------------------------------------------------------------------------
173
174    /**
175     *  Constructor.
176     *
177     *  @langversion 3.0
178     *  @playerversion Flash 9
179     *  @playerversion AIR 1.1
180     *  @productversion Flex 3
181     */
182    public function ColumnChart()
183    {
184        super();
185
186        LinearAxis(horizontalAxis).autoAdjust = false;
187
188        dataTipMode = "single";
189
190        seriesFilters = [ new DropShadowFilter(2, 45, 0.2 * 0xFFFFFF)];
191    }
192
193    //--------------------------------------------------------------------------
194    //
195    //  Variables
196    //
197    //--------------------------------------------------------------------------
198
199    /**
200     *  @private
201     */
202    private var _moduleFactoryInitialized:Boolean = false;
203
204
205    /**
206     *  @private
207     */
208    private var _perSeriescolumnWidthRatio:Number;
209
210    /**
211     *  @private
212     */
213    private var _perSeriesMaxColumnWidth:Number;
214
215    /**
216     *  @private
217     */
218    private var _leftOffset:Number;
219
220    /**
221     * @private
222     */
223    mx_internal var allLabelsMeasured: Boolean = false;
224
225    /**
226     * @private
227     */
228    private var _allItems:Array /* of ChartItem */ = [];
229
230    /**
231     * @private
232     */
233    private var _needLabels:Boolean = false;
234
235    /**
236     * @private
237     */
238    private var _tempField:Object;
239    //--------------------------------------------------------------------------
240    //
241    //  Properties
242    //
243    //--------------------------------------------------------------------------
244
245    //---------------------------------
246    // extendLabelToEnd
247    //---------------------------------
248    /**
249     * @private
250     * Storage for extendLabelToEnd property
251     */
252    private var _extendLabelToEnd:Boolean = false;
253
254    [Inspectable(category="General")]
255
256    /**
257     * Determines whether or not data labels can extend to the end of the chart.
258     * If you set this to true, labels can use the whole space between the item
259     * and the boundary of the chart to show its label. Otherwise, data labels are
260     * restricted to the area defined by their chart item.
261     *
262     * @default false
263     *
264     *  @langversion 3.0
265     *  @playerversion Flash 9
266     *  @playerversion AIR 1.1
267     *  @productversion Flex 3
268     */
269     public function get extendLabelToEnd():Boolean
270     {
271        return _extendLabelToEnd;
272     }
273
274    /**
275     * @private
276     */
277     public function set extendLabelToEnd(value:Boolean):void
278     {
279        _extendLabelToEnd = value;
280        measureLabels();
281     }
282
283    //---------------------------------
284    // maxLabelWidth
285    //---------------------------------
286
287    /**
288     * @private
289     * Storage for maxLabelWidth property
290     */
291    private var _maxLabelWidth:int = 50;
292
293    [Inspectable(category="General")]
294
295    /**
296     * Determines maximum width in pixels of label of items.
297     *
298     * @default 50
299     *
300     *  @langversion 3.0
301     *  @playerversion Flash 9
302     *  @playerversion AIR 1.1
303     *  @productversion Flex 3
304     */
305     public function get maxLabelWidth():int
306     {
307        return _maxLabelWidth;
308     }
309
310    /**
311     * @private
312     */
313     public function set maxLabelWidth(value:int):void
314     {
315        _maxLabelWidth = value;
316        measureLabels();
317     }
318
319    //---------------------------------
320    // showLabelVertically
321    //---------------------------------
322    /**
323     * @private
324     * Storage for showLabelVertically property
325     */
326    private var _showLabelVertically:Boolean = false;
327
328    [Inspectable(category="General")]
329
330    /**
331     * Determines whether or not the data labels can be shown vertically.
332     * If you set this to true and if embedded fonts are used, labels will be shown vertically
333     * if they cannot be fit horizontally within the column width.
334     *
335     * @default false
336     *
337     *  @langversion 3.0
338     *  @playerversion Flash 9
339     *  @playerversion AIR 1.1
340     *  @productversion Flex 3
341     */
342     public function get showLabelVertically():Boolean
343     {
344        return _showLabelVertically;
345     }
346
347    /**
348     * @private
349     */
350     public function set showLabelVertically(value:Boolean):void
351     {
352        _showLabelVertically = value;
353        measureLabels();
354     }
355
356    //----------------------------------
357    //  type
358    //----------------------------------
359
360    /**
361     *  @private
362     *  Storage for the type property.
363     */
364    private var _type:String = "clustered";
365
366    [Inspectable(category="General", enumeration="stacked,100%,clustered,overlaid", defaultValue="clustered")]
367
368    /**
369     *  The type of the column chart.
370     *
371     *  <p>Possible values are:</p>
372     *  <ul>
373     *    <li><code>"clustered"</code>:
374     *    Values from different series are grouped by category.
375     *    This is the default type.</li>
376     *    <li><code>"overlaid"</code>:
377     *    Multiple values are rendered on top of one another by category,
378     *    with the last series on top. </li>
379     *    <li><code>"stacked"</code>:
380     *    Columns are stacked on top of each other and grouped by category.
381     *    Each column represents the cumulative value
382     *    of the columns beneath it. </li>
383     *    <li><code>"100%"</code>:
384     *    Columns are stacked on top of each other, adding up to 100%.
385     *    Each column represents the percent that it contributes
386     *    to the sum of the values for that category.</li>
387     *  </ul>
388     *
389     *  @langversion 3.0
390     *  @playerversion Flash 9
391     *  @playerversion AIR 1.1
392     *  @productversion Flex 3
393     */
394    public function get type():String
395    {
396        return _type;
397    }
398
399    /**
400     *  @private
401     */
402    public function set type(value:String):void
403    {
404        if (_type == value)
405            return;
406
407        _type = value;
408
409        invalidateSeries();
410        invalidateData();
411    }
412
413    //--------------------------------------------------------------------------
414    //
415    //  Overridden methods: UIComponent
416    //
417    //--------------------------------------------------------------------------
418
419    /**
420     *  @private
421     */
422    private function initStyles():Boolean
423    {
424        HaloDefaults.init(styleManager);
425
426		var columnChartStyle:CSSStyleDeclaration = styleManager.getStyleDeclaration("mx.charts.ColumnChart");
427		columnChartStyle.setStyle("chartSeriesStyles", HaloDefaults.chartBaseChartSeriesStyles);
428		columnChartStyle.setStyle("fill", new SolidColor(0xFFFFFF, 0));
429		columnChartStyle.setStyle("calloutStroke", new SolidColorStroke(0x888888,2));
430		columnChartStyle.setStyle("horizontalAxisStyleNames", ["blockCategoryAxis"]);
431		columnChartStyle.setStyle("verticalAxisStyleNames", ["blockNumericAxis"]);
432
433        return true;
434    }
435
436
437    /**
438     *   A module factory is used as context for using embedded fonts and for finding the style manager that controls the styles for this component.
439     *
440     *  @langversion 3.0
441     *  @playerversion Flash 9
442     *  @playerversion AIR 1.1
443     *  @productversion Flex 3
444     */
445    override public function set moduleFactory(factory:IFlexModuleFactory):void
446    {
447        super.moduleFactory = factory;
448
449        if (_moduleFactoryInitialized)
450            return;
451
452        _moduleFactoryInitialized = true;
453
454        // our style settings
455        initStyles();
456    }
457
458    /**
459     * @private
460     */
461    override protected function createChildren():void
462    {
463        super.createChildren();
464        var labelClass:Class = getLabelClass();
465        _tempField = new labelClass();
466        _tempField.visible = false;
467        _tempField.text = "W...";
468        _tempField.toolTip = "";
469        addChild(_tempField as DisplayObject);
470        _tempField.validateNow();
471    }
472
473    private function getLabelClass():Class
474    {
475        var labelClass:Class = getStyle("labelClass");
476        if(labelClass == null)
477        {
478            try{
479                labelClass = Class(ApplicationDomain.currentDomain.
480                    getDefinition("spark.components::Label"));
481            }
482            catch(e:Error)
483            {
484                labelClass = Class(ApplicationDomain.currentDomain.
485                    getDefinition("mx.controls::Label"));
486            }
487        }
488        return labelClass;
489    }
490
491    /**
492     *  @private
493     */
494    override public function styleChanged(styleProp:String):void
495    {
496        if (styleProp == null || INVALIDATING_STYLES[styleProp] != undefined)
497            invalidateSeries();
498
499        super.styleChanged(styleProp);
500    }
501
502    //--------------------------------------------------------------------------
503    //
504    //  Overridden methods: ChartBase
505    //
506    //--------------------------------------------------------------------------
507
508    /**
509     *  @private
510     */
511    override protected function customizeSeries(seriesGlyph:Series, i:uint):void
512    {
513        if (seriesGlyph is ColumnSeries || seriesGlyph is ColumnSet)
514        {
515            var series:Object = seriesGlyph;
516
517            if (!isNaN(_perSeriescolumnWidthRatio))
518                series.columnWidthRatio = _perSeriescolumnWidthRatio;
519
520            if (!isNaN(_perSeriesMaxColumnWidth))
521                series.maxColumnWidth = _perSeriesMaxColumnWidth;
522
523            if (_type == "overlaid")
524                series.offset = 0;
525            else
526                series.offset = _leftOffset + i * _perSeriescolumnWidthRatio;
527
528            if (series is IStackable)
529            {
530                var stackSeries:IStackable = IStackable(series);
531                stackSeries.stacker = null;
532                stackSeries.stackTotals = null;
533            }
534        }
535    }
536
537    /**
538     *  @private
539     */
540    override protected function applySeriesSet(seriesSet:Array /* of Series */,
541                                               transform:DataTransform):Array /* of Series */
542    {
543        var columnWidthRatio:Number = getStyle("columnWidthRatio");
544        var maxColumnWidth:Number = getStyle("maxColumnWidth");
545        var g:IChartElement;
546
547        switch (_type)
548        {
549            case "stacked":
550            case "100%":
551            {
552                var n:int = seriesSet.length;
553                var i:int;
554                for (i = 0; i < n; i++)
555                {
556                    seriesSet[i].offset = 0;
557                }
558
559                var newSeriesGlyph:ColumnSet = new ColumnSet();
560                newSeriesGlyph.owner = this;
561                newSeriesGlyph.series = seriesSet;
562                for (i = 0; i < n; i++)
563                {
564                    g = seriesSet[i] as IChartElement;
565                    newSeriesGlyph.series[i].owner = newSeriesGlyph;
566                    if (!g)
567                        continue;
568                    if (g.labelContainer)
569                        newSeriesGlyph.labelContainer.addChild(seriesSet[i].labelContainer);
570                }
571
572                if (!isNaN(columnWidthRatio))
573                    newSeriesGlyph.columnWidthRatio = _perSeriescolumnWidthRatio;
574
575                if (!isNaN(maxColumnWidth))
576                    newSeriesGlyph.maxColumnWidth = _perSeriesMaxColumnWidth;
577
578                newSeriesGlyph.type = _type;
579
580                invalidateData();
581
582                return [ newSeriesGlyph ];
583            }
584
585            case "clustered":
586            default:
587            {
588
589                var columnSeriesCount:int = 0;
590                for each(var series:Series in seriesSet) {
591                    if(series is ColumnSet || series is ColumnSeries)
592                        columnSeriesCount++;
593                }
594
595                _perSeriescolumnWidthRatio = columnWidthRatio / columnSeriesCount;
596                _perSeriesMaxColumnWidth = maxColumnWidth / columnSeriesCount;
597
598                _leftOffset = (1 - columnWidthRatio) / 2 +
599                              _perSeriescolumnWidthRatio / 2 - 0.5;
600
601                n = seriesSet.length;
602                var count:int = 0;
603                for (i = 0; i < n; i++)
604                {
605                    var newSeries:IChartElement = seriesSet[i];
606                    if (newSeries is ColumnSeries || newSeries is ColumnSet)
607                    {
608                        customizeSeries(Series(seriesSet[i]), count);
609                        count++;
610                    }
611                }
612
613                return seriesSet;
614            }
615
616            case "overlaid":
617            {
618                _perSeriescolumnWidthRatio = columnWidthRatio;
619                _perSeriesMaxColumnWidth = maxColumnWidth;
620
621                _leftOffset = 0;
622                return super.applySeriesSet(seriesSet, transform);
623            }
624        }
625    }
626
627    /**
628     * Determines positions and dimensions of labels for all series in the chart
629     *
630     *  @langversion 3.0
631     *  @playerversion Flash 9
632     *  @playerversion AIR 1.1
633     *  @productversion Flex 3
634     */
635    override mx_internal function measureLabels():Object
636    {
637        getSeriesLabelPosSet();
638        if (!_needLabels)
639            return null;
640        var len:int = series.length;
641        var n:int;
642        var allSeriesTransform:Boolean = true;
643
644        if (type == "stacked" || type == "overlaid" || type == "100%")
645            allLabelsMeasured = false;
646
647        _allItems = [];
648        n = len;
649        for (var i:int = 0; i < n; i++)
650        {
651            findChartItems(series[i]);
652        }
653
654        _allItems.sort(sortOnX);    //sort all items with respect to their position along X-axis
655
656        var itemsLen:Number = _allItems.length;
657        n = itemsLen;
658        for (i = 0; i < n; i++)
659        {
660            var v:ColumnSeriesItem = _allItems[i];
661            var columnSeries:ColumnSeries = ColumnSeries(ColumnSeriesItem(_allItems[i]).element);
662            var labelPosition:String = columnSeries.labelPos;
663
664            if (labelPosition == "inside" || labelPosition == "outside")
665            {
666                var base:Number = columnSeries.seriesRenderData.renderedBase;
667                var size:Number = columnSeries.getStyle('fontSize');
668                if (columnSeries.labelFunction != null)
669                    columnSeries.measuringField.text = v.labelText = columnSeries.labelFunction(v, columnSeries);
670                else if (columnSeries.labelField != null)
671                    columnSeries.measuringField.text = v.labelText = v.item[columnSeries.labelField];
672                else if (columnSeries.dataFunction != null)
673                    columnSeries.measuringField.text = v.labelText = columnSeries.dataFunction(columnSeries, v.item, 'yNumber');
674                else
675                    columnSeries.measuringField.text = v.labelText = v.item[columnSeries.yField];
676
677                columnSeries.measuringField.validateNow();
678
679                var labelRotation:Number = columnSeries.labelAngle;
680                var labelSizeLimit:Number = columnSeries.maxLabelSize;
681
682                if (labelPosition == "outside" && (type == "overlaid" || type == "stacked" || type == "100%"))
683                {
684                    columnSeries.labelPos = 'inside';
685                    labelPosition = "inside";
686                        // today, labelPosition = inside is only supported for 100%, stacked and overlaid charts
687                }
688
689
690                if (labelPosition == 'outside')
691                {
692                    if (!showLabelVertically)
693                    {
694                        v.labelIsHorizontal = true;
695                        if (extendLabelToEnd)
696                        {
697                            v.labelWidth = Math.abs((this.dataRegion.right -
698                                           (v.x + columnSeries.seriesRenderData.renderedXOffset -
699                                           columnSeries.seriesRenderData.renderedHalfWidth))/ Math.cos(0));
700                        }
701                        else
702                        {
703                            v.labelWidth = Math.min(Math.abs((this.dataRegion.right -
704                                           (v.x + columnSeries.seriesRenderData.renderedXOffset -
705                                           columnSeries.seriesRenderData.renderedHalfWidth))/ Math.cos(0)),maxLabelWidth);
706                        }
707                        v.labelHeight = columnSeries.measuringField.textHeight + 2;
708                    }
709                    else
710                    {                   //check whether labels can be rendered horizontally
711                        v.labelIsHorizontal = true;
712                        v.labelWidth = 2 * columnSeries.seriesRenderData.renderedHalfWidth;
713                        v.labelHeight = columnSeries.measuringField.textHeight + 2;
714
715                        if (columnSeries.measuringField.textWidth + 5 > 2 * columnSeries.seriesRenderData.renderedHalfWidth &&
716                        columnSeries.measuringField.embedFonts)
717                        {
718                            v.labelIsHorizontal = false;
719                            v.labelHeight = 2 * columnSeries.seriesRenderData.renderedHalfWidth;
720                            if (!(isNaN(labelRotation)) && labelRotation!=-90)
721                            {
722                                var r:Number = -labelRotation / Math.PI * 180;
723                                        //for future enhancement
724                                        //check for type of chart if we need to support labelPosition = outside for all types of charts
725                                if (type == "clustered")
726                                {
727                                    if (i < itemsLen - len)
728                                    {
729                                        v.labelWidth = Math.abs((_allItems[i + len - i%len].x + ColumnSeries(_allItems[i + len - i%len].element).seriesRenderData.renderedXOffset
730                                                       - ColumnSeries(_allItems[i + len - i%len].element).seriesRenderData.renderedHalfWidth -
731                                                       (v.x + columnSeries.seriesRenderData.renderedXOffset -
732                                                       columnSeries.seriesRenderData.renderedHalfWidth))/ Math.cos(r));
733                                    }
734                                    else
735                                        v.labelWidth = Math.abs((_allItems[itemsLen -1].x + ColumnSeries(_allItems[itemsLen - 1].element).seriesRenderData.renderedXOffset
736                                                       + ColumnSeries(_allItems[itemsLen - 1].element).seriesRenderData.renderedHalfWidth -
737                                                       (v.x + columnSeries.seriesRenderData.renderedXOffset -
738                                                       columnSeries.seriesRenderData.renderedHalfWidth))/ Math.cos(r));
739                                }
740                            }
741                            else
742                            {
743                                if (v.y < (isNaN(v.min) ? base : v.min))
744                                {
745                                    v.labelWidth = columnSeries.dataToLocal(0,v.yNumber).y - columnSeries.dataToLocal(0,NumericAxis(columnSeries.dataTransform.getAxis(CartesianTransform.VERTICAL_AXIS)).computedMaximum).y;
746                                    v.labelY = v.y;
747                                    if (v.labelY < this.dataRegion.top)
748                                    {
749                                        v.labelWidth = Math.abs(v.y -(isNaN(v.min) ? base : v.min));
750                                        v.labelY = isNaN(v.min) ? base : v.min;
751                                    }
752                                }
753                                else
754                                {
755                                    v.labelWidth = columnSeries.dataToLocal(0,NumericAxis(columnSeries.dataTransform.getAxis(CartesianTransform.VERTICAL_AXIS)).computedMinimum).y - columnSeries.dataToLocal(0,v.yNumber).y;
756                                    v.labelY = v.y + v.labelWidth;
757                                    if (v.labelY > this.dataRegion.bottom)
758                                    {
759                                        v.labelWidth = Math.abs(v.y -(isNaN(v.min) ? base : v.min));
760                                        v.labelY = v.y;
761                                    }
762                                }
763                            }
764
765                            v.labelX = v.x - columnSeries.seriesRenderData.renderedHalfWidth + columnSeries.seriesRenderData.renderedXOffset;
766                        }
767                    }
768
769                    var labelScale:Number = 1;
770                    if (columnSeries.measuringField.textWidth + 5 > v.labelWidth || (columnSeries.measuringField.textHeight) > v.labelHeight)
771                    {
772                        labelScale = v.labelWidth / (columnSeries.measuringField.textWidth + 5);
773                        labelScale = Math.min(labelScale, v.labelHeight / (columnSeries.measuringField.textHeight));
774                        if (size * labelScale > labelSizeLimit)
775                        {
776                            columnSeries.seriesRenderData.labelScale = Math.min(labelScale,columnSeries.seriesRenderData.labelScale);
777                            /* if (v.labelIsHorizontal)
778                            {
779                                _tempField.setStyle('fontSize',size * columnSeries.seriesRenderData.labelScale);
780                                v.labelHeight = _tempField.textHeight + 2;
781                            } */
782                        }
783                        else
784                        {
785                            _tempField.setStyle('fontSize',size);
786                            _tempField.validateNow();
787                            if (_tempField.measuredWidth > v.labelWidth || columnSeries.measuringField.textHeight > v.labelHeight)
788                            {
789                                labelScale = v.labelWidth / _tempField.measuredWidth;
790                                labelScale = Math.min(labelScale, v.labelHeight / (columnSeries.measuringField.measuredHeight));
791                                if (size * labelScale > labelSizeLimit)
792                                {
793                                    columnSeries.seriesRenderData.labelScale = Math.min(labelScale,columnSeries.seriesRenderData.labelScale);
794                                    /* if (v.labelIsHorizontal)
795                                    {
796                                        _tempField.setStyle('fontSize',size * columnSeries.seriesRenderData.labelScale);
797                                        v.labelHeight = _tempField.textHeight + 2;
798                                    } */
799                                }
800                                else
801                                {
802                                    v.labelText = "";
803                                    v.labelWidth = 1;
804                                    v.labelHeight = 0;
805                                }
806                            }
807
808                        }
809                    }
810                    if (!isNaN(labelRotation) && labelRotation!=-90 && columnSeries.measuringField.embedFonts)
811                    {
812                        v.labelHeight = columnSeries.measuringField.textHeight + 2;
813                        if (v.y < (isNaN(v.min) ? base : v.min))
814                            v.labelY = v.y - v.labelHeight;
815                        else
816                            v.labelY = v.y + v.labelHeight;
817                    }
818                    if (v.labelIsHorizontal)
819                    {
820                        if (v.y < (isNaN(v.min) ? base : v.min))
821                        {
822                            v.labelY = v.y - v.labelHeight;
823                            if (v.labelY < this.dataRegion.top)
824                                v.labelY = v.y;
825                        }
826
827                        else
828                        {
829                            v.labelY = v.y;
830                            if (v.labelY > this.dataRegion.bottom)
831                                v.labelY = v.y - v.labelHeight;
832
833                        }
834                        v.labelX = v.x - columnSeries.seriesRenderData.renderedHalfWidth +columnSeries.seriesRenderData.renderedXOffset;
835                    }
836                    v.labelTextWidth = columnSeries.measuringField.textWidth;
837                    var j:int = 0;
838                    if (!extendLabelToEnd)
839                    {
840                        //maxLabelWidth
841                        for (j = (i-1); j >= 1;)
842                        {
843                            if (_allItems[j].x >= v.x - maxLabelWidth)
844                                j--;
845                            else
846                                break;
847                        }
848                        if (j < 0)
849                            j = i;
850                    }
851
852                    for (var k:int = j; k < i; k++)
853                    {
854                        var tempItem:ColumnSeriesItem = _allItems[k];
855                                    //test for overlaps with previous labels
856                        if (tempItem.labelIsHorizontal)
857                        {
858                            if (v.labelX + 0.0001 < Math.min(tempItem.labelX + tempItem.labelWidth , tempItem.labelX + tempItem.labelTextWidth))
859                            {
860                                if (v.labelIsHorizontal)
861                                {
862                                    if ((((v.labelY + v.labelHeight) > tempItem.labelY) && ((v.labelY + v.labelHeight) < (tempItem.labelY + tempItem.labelHeight))) ||
863                                        ((v.labelY > tempItem.labelY) && (v.labelY < (tempItem.labelY + tempItem.labelHeight))) ||
864                                        ((v.labelY == tempItem.labelY) && ((tempItem.labelY + tempItem.labelHeight) == (v.labelY + v.labelHeight)) && (v.labelX > tempItem.labelX) && (v.labelX < (tempItem.labelX + tempItem.labelWidth))) ||
865                                        ((v.labelY <= tempItem.labelY) && ((v.labelY + v.labelHeight) > (tempItem.labelY + tempItem.labelHeight))) ||
866                                        ((v.labelY < tempItem.labelY) && ((v.labelY + v.labelHeight) >= (tempItem.labelY + tempItem.labelHeight))))
867                                    {
868                                        if (v.y < (isNaN(v.min) ? base : v.min))
869                                            v.labelY = tempItem.labelY - v.labelHeight;
870                                        else
871                                            v.labelY = tempItem.labelY + tempItem.labelHeight;
872                                    }
873                                }
874                                else
875                                {
876                                    if (((v.labelY > tempItem.labelY) && ((v.labelY - v.labelWidth) < (tempItem.labelY))) ||
877                                        (((v.labelY - v.labelWidth) >= tempItem.labelY) && (v.labelY > (tempItem.labelY - tempItem.labelHeight))))
878                                    {
879                                        var prevY:Number = v.labelY;
880                                        if (v.y < (isNaN(v.min) ? base : v.min))
881                                        {
882                                            v.labelY = tempItem.labelY - tempItem.labelHeight;
883                                            v.labelWidth = v.labelWidth - (prevY - v.labelY);
884                                        }
885                                        else
886                                        {
887                                            v.labelY = tempItem.labelY + tempItem.labelHeight;
888                                            v.labelWidth = v.labelWidth - (v.labelY - prevY);
889                                        }
890                                    }
891                                }
892                            }
893                        }
894                    }
895                }
896                else if (labelPosition == "inside")
897                {
898                    v.labelIsHorizontal = true;
899                    v.labelWidth = 2 * columnSeries.seriesRenderData.renderedHalfWidth;
900                    v.labelHeight = columnSeries.measuringField.textHeight + 2;
901
902                    if (columnSeries.measuringField.textWidth + 5 > 2 * columnSeries.seriesRenderData.renderedHalfWidth &&
903                        columnSeries.measuringField.embedFonts)
904                    {
905                        v.labelIsHorizontal = false;
906                        v.labelWidth = Math.abs(v.y -(isNaN(v.min) ? base : v.min));
907                        v.labelHeight = 2 * columnSeries.seriesRenderData.renderedHalfWidth;
908                        if (v.y < (isNaN(v.min) ? base : v.min))
909                            v.labelY = isNaN(v.min) ? base : v.min;
910                        else
911                            v.labelY = v.y;
912
913                        v.labelX = v.x - v.labelHeight/2 + columnSeries.seriesRenderData.renderedXOffset;
914                    }
915
916                    labelScale = 1;
917                    if (columnSeries.measuringField.textWidth + 5 > v.labelWidth || columnSeries.measuringField.textHeight > v.labelHeight)
918                    {
919                        labelScale = v.labelWidth / (columnSeries.measuringField.textWidth + 5);
920                        labelScale = Math.min(labelScale, v.labelHeight / columnSeries.measuringField.textHeight);
921                        if (size * labelScale > labelSizeLimit)
922                        {
923                            columnSeries.seriesRenderData.labelScale = Math.min(labelScale,columnSeries.seriesRenderData.labelScale);
924                            /* if (v.labelIsHorizontal)
925                            {
926                                _tempField.setStyle('fontSize',size * columnSeries.seriesRenderData.labelScale);
927                                v.labelHeight = _tempField.textHeight + 2;
928                            } */
929                        }
930                        else
931                        {
932                            _tempField.setStyle('fontSize',size);
933                            _tempField.validateNow();
934                            if (_tempField.measuredWidth > v.labelWidth || columnSeries.measuringField.textHeight > v.labelHeight)
935                            {
936                                labelScale = v.labelWidth / _tempField.measuredWidth;
937                                labelScale = Math.min(labelScale, v.labelHeight / (columnSeries.measuringField.textHeight));
938                                if (size * labelScale > labelSizeLimit)
939                                {
940                                    columnSeries.seriesRenderData.labelScale = Math.min(labelScale,columnSeries.seriesRenderData.labelScale);
941                                    /* if (v.labelIsHorizontal)
942                                    {
943                                        _tempField.setStyle('fontSize',size * columnSeries.seriesRenderData.labelScale);
944                                        v.labelHeight = _tempField.textHeight + 2;
945                                    } */
946                                }
947                                else
948                                {
949                                    v.labelText = "";
950                                    v.labelWidth = 1;
951                                    v.labelHeight = 0;
952                                }
953                            }
954                        }
955                    }
956                    if (v.labelIsHorizontal)
957                    {
958                        var align:String = columnSeries.getStyle('labelAlign');
959                        if (align == "top")
960                        {
961                            if (v.y < (isNaN(v.min) ? base : v.min))
962                                v.labelY = v.y;
963                            else
964                                v.labelY = v.y - v.labelHeight;
965                        }
966                        else if (align == "bottom")
967                        {
968                            if (v.y < (isNaN(v.min) ? base : v.min))
969                                v.labelY = (isNaN(v.min) ? base : v.min) - v.labelHeight;
970                            else
971                                v.labelY = isNaN(v.min) ? base : v.min;
972                        }
973                        else
974                        {
975                            if (v.y < (isNaN(v.min) ? base : v.min))
976                                v.labelY = v.y + Math.abs(v.y - (isNaN(v.min) ? base : v.min))/2 - v.labelHeight/2;
977                            else
978                                v.labelY = v.y - Math.abs(v.y - (isNaN(v.min) ? base : v.min))/2 + v.labelHeight/2;
979                        }
980                        v.labelX = v.x - columnSeries.seriesRenderData.renderedHalfWidth + columnSeries.seriesRenderData.renderedXOffset;
981                    }
982                    v.labelTextWidth = columnSeries.measuringField.textWidth;
983                    j = 0;
984                    if (!extendLabelToEnd)
985                    {
986                        //maxLabelWidth
987                        for (j = (i-1); j >= 1;)
988                        {
989                            if (_allItems[j].x >= v.x - maxLabelWidth)
990                                j--;
991                            else
992                                break;
993                        }
994                        if (j < 0)
995                            j = i;
996                    }
997
998                    for (k = j; k < i; k++)
999                    {
1000                        tempItem = _allItems[k];
1001                        if (tempItem.labelIsHorizontal)
1002                        {
1003                            if (v.labelX + 0.0001 < Math.min(tempItem.labelX + tempItem.labelWidth , tempItem.labelX + tempItem.labelTextWidth))
1004                            {
1005                                if (v.labelIsHorizontal)
1006                                {
1007                                    if ((((v.labelY + v.labelHeight) > tempItem.labelY) && ((v.labelY + v.labelHeight) < (tempItem.labelY + tempItem.labelHeight))) ||
1008                                        ((v.labelY > tempItem.labelY) && (v.labelY < (tempItem.labelY + tempItem.labelHeight))) ||
1009                                        ((v.labelY == tempItem.labelY) && ((tempItem.labelY + tempItem.labelHeight) == (v.labelY + v.labelHeight)) && (v.labelX > tempItem.labelX) && (v.labelX < (tempItem.labelX + tempItem.labelWidth))) ||
1010                                        ((v.labelY <= tempItem.labelY) && ((v.labelY + v.labelHeight) > (tempItem.labelY + tempItem.labelHeight))) ||
1011                                        ((v.labelY < tempItem.labelY) && ((v.labelY + v.labelHeight) >= (tempItem.labelY + tempItem.labelHeight))))
1012                                    {
1013                                        if (v.y < (isNaN(v.min) ? base : v.min))
1014                                            v.labelY = tempItem.labelY - v.labelHeight;
1015                                        else
1016                                            v.labelY = tempItem.labelY + tempItem.labelHeight;
1017                                    }
1018                                    else if (((v.labelY < tempItem.labelY) && ((v.labelY + v.labelHeight) > tempItem.labelY)) ||
1019                                        ((v.labelY > tempItem.labelY) && (v.labelY < (tempItem.labelY + tempItem.labelHeight))) ||
1020                                        (v.labelY == tempItem.labelY))
1021                                    {
1022                                        if (v.yNumber >= tempItem.yNumber)
1023                                        {
1024                                            tempItem.labelText = "";
1025                                            tempItem.labelWidth = 1;
1026                                        }
1027                                        else
1028                                        {
1029                                            v.labelText = "";
1030                                            v.labelWidth = 1;
1031                                        }
1032                                    }
1033                                }
1034                                else
1035                                {
1036                                    if (((v.labelY > tempItem.labelY) && ((v.labelY - Math.min(v.labelWidth,v.labelTextWidth)) < (tempItem.labelY))) ||
1037                                        (((v.labelY - v.labelWidth) >= tempItem.labelY) && (v.labelY > (tempItem.labelY - tempItem.labelHeight))))
1038                                    {
1039                                        prevY = v.labelY;
1040                                        if (v.y < (isNaN(v.min) ? base : v.min))
1041                                        {
1042                                            v.labelY = tempItem.labelY - tempItem.labelHeight;
1043                                            v.labelWidth = v.labelWidth - (prevY - v.labelY);
1044                                        }
1045                                        else
1046                                        {
1047                                            v.labelY = tempItem.labelY + tempItem.labelHeight;
1048                                            v.labelWidth = v.labelWidth - (v.labelY - prevY);
1049                                        }
1050                                    }
1051                                }
1052                            }
1053                        }
1054                        else
1055                        {
1056                            if (v.labelX == tempItem.labelX)
1057                            {
1058                                if (((v.labelY <= tempItem.labelY) && (v.labelY > (tempItem.labelY - Math.min(tempItem.labelWidth, tempItem.labelTextWidth) + 0.0001))) ||
1059                                    ((v.labelY >= tempItem.labelY) && ((v.labelY - Math.min(v.labelWidth, v.labelTextWidth) + 0.0001) < tempItem.labelY)))
1060                                {
1061                                    if (v.yNumber < tempItem.yNumber)
1062                                    {
1063                                        v.labelText = "";
1064                                        v.labelWidth = 1;
1065                                    }
1066                                    else
1067                                    {
1068                                        tempItem.labelText = "";
1069                                        tempItem.labelWidth = 1;
1070                                    }
1071                                }
1072                            }
1073                        }
1074                    }
1075                }
1076            }
1077            else
1078            {
1079                columnSeries.seriesRenderData.labelData = null;
1080                columnSeries.labelCache.count = 0;
1081            }
1082        }
1083        allLabelsMeasured = true;
1084        n = len;
1085        for (i = 0; i < n; i++)
1086        {
1087            invalidateDisplay(series[i]);
1088        }
1089        return null;
1090    }
1091
1092
1093    //---------------------------------------------------------------------------------
1094    //
1095    // Methods
1096    //
1097    //---------------------------------------------------------------------------------
1098    mx_internal function getSeriesLabelPos(series:Series):void
1099    {
1100        if (series is ColumnSeries)
1101        {
1102            var columnSeries:ColumnSeries = ColumnSeries(series);
1103            var position:String = columnSeries.labelPos;
1104            if (position == "inside" || position == "outside" || position == "none")
1105                _needLabels = true;
1106        }
1107        else if (series is ColumnSet)
1108        {
1109            var setSeries:Array /* of Series */ = ColumnSet(series).series;
1110            var n:int  = setSeries.length;
1111            for (var i:int = 0; i < n; i++)
1112            {
1113                getSeriesLabelPos(setSeries[i]);
1114            }
1115        }
1116    }
1117
1118    mx_internal function getSeriesLabelPosSet():void
1119    {
1120        var n:int = series.length;
1121        _needLabels = false;
1122        for (var i:int = 0; i < n; i++)
1123        {
1124            if (_needLabels)
1125                return;
1126            getSeriesLabelPos(series[i]);
1127        }
1128    }
1129
1130    /**
1131     * @private
1132     */
1133     private function sortOnX(a:ColumnSeriesItem,b:ColumnSeriesItem):int
1134     {
1135        var offset1:Number = ColumnSeries(a.element).seriesRenderData.renderedXOffset;
1136        var offset2:Number = ColumnSeries(b.element).seriesRenderData.renderedXOffset;
1137        if (a.x + offset1 > b.x + offset2)
1138            return 1;
1139        else if (a.x + offset1 < b.x + offset2)
1140            return -1;
1141        else
1142        {
1143            if (a.yNumber > b.yNumber)
1144                return 1;
1145            else if (a.yNumber < b.yNumber)
1146                return -1;
1147            else
1148                return 0;
1149        }
1150
1151     }
1152
1153     /**
1154     * @private
1155     */
1156     private function findChartItems(series:Series):void
1157     {
1158        var n:int;
1159        var i:int;
1160        if (series is ColumnSeries)
1161        {
1162            var columnSeries:ColumnSeries = ColumnSeries(series);
1163            var seriesItems:Array /* of ColumnSeriesItem */ = columnSeries.seriesRenderData.filteredCache;
1164            n = seriesItems.length;
1165            columnSeries.labelCache.count = n;
1166            columnSeries.seriesRenderData.labelData = columnSeries.labelContainer;
1167            columnSeries.seriesRenderData.labelScale = 1;
1168            for (i = 0; i < n; i++)
1169            {
1170                _allItems.push(ColumnSeriesItem(seriesItems[i]));
1171            }
1172        }
1173        else if (series is ColumnSet)
1174        {
1175            var setSeries:Array /* of Series */ = ColumnSet(series).series;
1176            n = setSeries.length;
1177            for (i = 0; i < n; i++)
1178            {
1179                findChartItems(setSeries[i]);
1180            }
1181        }
1182     }
1183    /**
1184     * @private
1185     */
1186     private function invalidateDisplay(series:Series):void
1187     {
1188        if (series is ColumnSeries)
1189            ColumnSeries(series).updateLabels();
1190        else if (series is ColumnSet)
1191        {
1192            var setSeries:Array /* of Series */ = ColumnSet(series).series;
1193            var n:int = setSeries.length;
1194            for (var i:int = 0; i < n; i++)
1195            {
1196                invalidateDisplay(setSeries[i]);
1197            }
1198        }
1199     }
1200}
1201
1202}
1203