Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('axis-numeric-base', function (Y, NAME) {
2
 
3
/**
4
 * Provides functionality for the handling of numeric axis data for a chart.
5
 *
6
 * @module charts
7
 * @submodule axis-numeric-base
8
 */
9
var Y_Lang = Y.Lang;
10
 
11
/**
12
 * NumericImpl contains logic for numeric data. NumericImpl is used by the following classes:
13
 * <ul>
14
 *      <li>{{#crossLink "NumericAxisBase"}}{{/crossLink}}</li>
15
 *      <li>{{#crossLink "NumericAxis"}}{{/crossLink}}</li>
16
 *  </ul>
17
 *
18
 * @class NumericImpl
19
 * @constructor
20
 * @submodule axis-numeric-base
21
 */
22
function NumericImpl()
23
{
24
}
25
 
26
NumericImpl.NAME = "numericImpl";
27
 
28
NumericImpl.ATTRS = {
29
    /**
30
     * Indicates whether 0 should always be displayed.
31
     *
32
     * @attribute alwaysShowZero
33
     * @type Boolean
34
     */
35
    alwaysShowZero: {
36
        value: true
37
    },
38
 
39
    /**
40
     * Method used for formatting a label. This attribute allows for the default label formatting method to overridden.
41
     * The method use would need to implement the arguments below and return a `String` or an `HTMLElement`. The default
42
     * implementation of the method returns a `String`. The output of this method will be rendered to the DOM using
43
     * `appendChild`. If you override the `labelFunction` method and return an html string, you will also need to override
44
     * the Data' `appendLabelFunction` to accept html as a `String`.
45
     * <dl>
46
     *      <dt>val</dt><dd>Label to be formatted. (`String`)</dd>
47
     *      <dt>format</dt><dd>Object containing properties used to format the label. (optional)</dd>
48
     * </dl>
49
     *
50
     * @attribute labelFunction
51
     * @type Function
52
     */
53
 
54
    /**
55
     * Object containing properties used by the `labelFunction` to format a
56
     * label.
57
     *
58
     * @attribute labelFormat
59
     * @type Object
60
     */
61
    labelFormat: {
62
        value: {
63
            prefix: "",
64
            thousandsSeparator: "",
65
            decimalSeparator: "",
66
            decimalPlaces: "0",
67
            suffix: ""
68
        }
69
    },
70
 
71
    /**
72
     *Indicates how to round unit values.
73
     *  <dl>
74
     *      <dt>niceNumber</dt><dd>Units will be smoothed based on the number of ticks and data range.</dd>
75
     *      <dt>auto</dt><dd>If the range is greater than 1, the units will be rounded.</dd>
76
     *      <dt>numeric value</dt><dd>Units will be equal to the numeric value.</dd>
77
     *      <dt>null</dt><dd>No rounding will occur.</dd>
78
     *  </dl>
79
     *
80
     * @attribute roundingMethod
81
     * @type String
82
     * @default niceNumber
83
     */
84
    roundingMethod: {
85
        value: "niceNumber"
86
    },
87
 
88
    /**
89
     * Indicates the scaling for the chart. The default value is `linear`. For a logarithmic axis, set the value
90
     * to `logarithmic`.
91
     *
92
     * @attribute
93
     * @type String
94
     * @default linear
95
     */
96
    scaleType: {
97
        value: "linear"
98
    }
99
};
100
 
101
NumericImpl.prototype = {
102
    /**
103
     * @method initializer
104
     * @private
105
     */
106
    initializer: function() {
107
        this.after("alwaysShowZeroChange", this._keyChangeHandler);
108
        this.after("roundingMethodChange", this._keyChangeHandler);
109
        this.after("scaleTypeChange", this._keyChangeHandler);
110
    },
111
 
112
    /**
113
     * Formats a label based on the axis type and optionally specified format.
114
     *
115
     * @method
116
     * @param {Object} value
117
     * @param {Object} format Pattern used to format the value.
118
     * @return String
119
     */
120
    formatLabel: function(val, format)
121
    {
122
        if(format)
123
        {
124
            return Y.DataType.Number.format(val, format);
125
        }
126
        return val;
127
    },
128
 
129
    /**
130
     * Returns the sum of all values per key.
131
     *
132
     * @method getTotalByKey
133
     * @param {String} key The identifier for the array whose values will be calculated.
134
     * @return Number
135
     */
136
    getTotalByKey: function(key)
137
    {
138
        var total = 0,
139
            values = this.getDataByKey(key),
140
            i = 0,
141
            val,
142
            len = values ? values.length : 0;
143
        for(; i < len; ++i)
144
        {
145
           val = parseFloat(values[i]);
146
           if(!isNaN(val))
147
           {
148
                total += val;
149
           }
150
        }
151
        return total;
152
    },
153
 
154
    /**
155
     * Returns the value corresponding to the origin on the axis.
156
     *
157
     * @method getOrigin
158
     * @return Number
159
     */
160
    getOrigin: function() {
161
        var origin = 0,
162
            min = this.get("minimum"),
163
            max = this.get("maximum");
164
        origin = Math.max(origin, min);
165
        origin = Math.min(origin, max);
166
        return origin;
167
    },
168
 
169
    /**
170
     * Type of data used in `Data`.
171
     *
172
     * @property _type
173
     * @readOnly
174
     * @private
175
     */
176
    _type: "numeric",
177
 
178
    /**
179
     * Helper method for getting a `roundingUnit` when calculating the minimum and maximum values.
180
     *
181
     * @method _getMinimumUnit
182
     * @param {Number} max Maximum number
183
     * @param {Number} min Minimum number
184
     * @param {Number} units Number of units on the axis
185
     * @return Number
186
     * @private
187
     */
188
    _getMinimumUnit:function(max, min, units)
189
    {
190
        return this._getNiceNumber(Math.ceil((max - min)/units));
191
    },
192
 
193
    /**
194
     * Calculates a nice rounding unit based on the range.
195
     *
196
     * @method _getNiceNumber
197
     * @param {Number} roundingUnit The calculated rounding unit.
198
     * @return Number
199
     * @private
200
     */
201
    _getNiceNumber: function(roundingUnit)
202
    {
203
        var tempMajorUnit = roundingUnit,
204
            order = Math.ceil(Math.log(tempMajorUnit) * 0.4342944819032518),
205
            roundedMajorUnit = Math.pow(10, order),
206
            roundedDiff;
207
 
208
        if (roundedMajorUnit / 2 >= tempMajorUnit)
209
        {
210
            roundedDiff = Math.floor((roundedMajorUnit / 2 - tempMajorUnit) / (Math.pow(10,order-1)/2));
211
            tempMajorUnit = roundedMajorUnit/2 - roundedDiff*Math.pow(10,order-1)/2;
212
        }
213
        else
214
        {
215
            tempMajorUnit = roundedMajorUnit;
216
        }
217
        if(!isNaN(tempMajorUnit))
218
        {
219
            return tempMajorUnit;
220
        }
221
        return roundingUnit;
222
 
223
    },
224
 
225
    /**
226
     * Calculates the maximum and minimum values for the `Data`.
227
     *
228
     * @method _updateMinAndMax
229
     * @private
230
     */
231
    _updateMinAndMax: function()
232
    {
233
        var data = this.get("data"),
234
            max,
235
            min,
236
            len,
237
            num,
238
            i = 0,
239
            setMax = this.get("setMax"),
240
            setMin = this.get("setMin");
241
        if(!setMax || !setMin)
242
        {
243
            if(data && data.length && data.length > 0)
244
            {
245
                len = data.length;
246
                for(; i < len; i++)
247
                {
248
                    num = data[i];
249
                    if(isNaN(num))
250
                    {
251
                        max = setMax ? this._setMaximum : max;
252
                        min = setMin ? this._setMinimum : min;
253
                        continue;
254
                    }
255
 
256
                    if(setMin)
257
                    {
258
                        min = this._setMinimum;
259
                    }
260
                    else if(min === undefined)
261
                    {
262
                        min = num;
263
                    }
264
                    else
265
                    {
266
                        min = Math.min(num, min);
267
                    }
268
                    if(setMax)
269
                    {
270
                        max = this._setMaximum;
271
                    }
272
                    else if(max === undefined)
273
                    {
274
                        max = num;
275
                    }
276
                    else
277
                    {
278
                        max = Math.max(num, max);
279
                    }
280
 
281
                    this._actualMaximum = max;
282
                    this._actualMinimum = min;
283
                }
284
            }
285
            if(this.get("scaleType") !== "logarithmic")
286
            {
287
                this._roundMinAndMax(min, max, setMin, setMax);
288
            }
289
            else
290
            {
291
                this._dataMaximum = max;
292
                this._dataMinimum = min;
293
            }
294
        }
295
    },
296
 
297
    /**
298
     * Rounds the mimimum and maximum values based on the `roundingUnit` attribute.
299
     *
300
     * @method _roundMinAndMax
301
     * @param {Number} min Minimum value
302
     * @param {Number} max Maximum value
303
     * @private
304
     */
305
    _roundMinAndMax: function(min, max, setMin, setMax)
306
    {
307
        var roundingUnit,
308
            minimumRange,
309
            minGreaterThanZero = min >= 0,
310
            maxGreaterThanZero = max > 0,
311
            dataRangeGreater,
312
            maxRound,
313
            minRound,
314
            topTicks,
315
            botTicks,
316
            tempMax,
317
            tempMin,
318
            units = this.getTotalMajorUnits() - 1,
319
            alwaysShowZero = this.get("alwaysShowZero"),
320
            roundingMethod = this.get("roundingMethod"),
321
            useIntegers = (max - min)/units >= 1;
322
        if(roundingMethod)
323
        {
324
            if(roundingMethod === "niceNumber")
325
            {
326
                roundingUnit = this._getMinimumUnit(max, min, units);
327
                if(minGreaterThanZero && maxGreaterThanZero)
328
                {
329
                    if((alwaysShowZero || min < roundingUnit) && !setMin)
330
                    {
331
                        min = 0;
332
                        roundingUnit = this._getMinimumUnit(max, min, units);
333
                    }
334
                    else
335
                    {
336
                       min = this._roundDownToNearest(min, roundingUnit);
337
                    }
338
                    if(setMax)
339
                    {
340
                        if(!alwaysShowZero)
341
                        {
342
                            min = max - (roundingUnit * units);
343
                        }
344
                    }
345
                    else if(setMin)
346
                    {
347
                        max = min + (roundingUnit * units);
348
                    }
349
                    else
350
                    {
351
                        max = this._roundUpToNearest(max, roundingUnit);
352
                    }
353
                }
354
                else if(maxGreaterThanZero && !minGreaterThanZero)
355
                {
356
                    if(alwaysShowZero)
357
                    {
358
                        topTicks = Math.round(units/((-1 * min)/max + 1));
359
                        topTicks = Math.max(Math.min(topTicks, units - 1), 1);
360
                        botTicks = units - topTicks;
361
                        tempMax = Math.ceil( max/topTicks );
362
                        tempMin = Math.floor( min/botTicks ) * -1;
363
 
364
                        if(setMin)
365
                        {
366
                            while(tempMin < tempMax && botTicks >= 0)
367
                            {
368
                                botTicks--;
369
                                topTicks++;
370
                                tempMax = Math.ceil( max/topTicks );
371
                                tempMin = Math.floor( min/botTicks ) * -1;
372
                            }
373
                            //if there are any bottom ticks left calcualate the maximum by multiplying by the tempMin value
374
                            //if not, it's impossible to ensure that a zero is shown. skip it
375
                            if(botTicks > 0)
376
                            {
377
                                max = tempMin * topTicks;
378
                            }
379
                            else
380
                            {
381
                                max = min + (roundingUnit * units);
382
                            }
383
                        }
384
                        else if(setMax)
385
                        {
386
                            while(tempMax < tempMin && topTicks >= 0)
387
                            {
388
                                botTicks++;
389
                                topTicks--;
390
                                tempMin = Math.floor( min/botTicks ) * -1;
391
                                tempMax = Math.ceil( max/topTicks );
392
                            }
393
                            //if there are any top ticks left calcualate the minimum by multiplying by the tempMax value
394
                            //if not, it's impossible to ensure that a zero is shown. skip it
395
                            if(topTicks > 0)
396
                            {
397
                                min = tempMax * botTicks * -1;
398
                            }
399
                            else
400
                            {
401
                                min = max - (roundingUnit * units);
402
                            }
403
                        }
404
                        else
405
                        {
406
                            roundingUnit = Math.max(tempMax, tempMin);
407
                            roundingUnit = this._getNiceNumber(roundingUnit);
408
                            max = roundingUnit * topTicks;
409
                            min = roundingUnit * botTicks * -1;
410
                        }
411
                    }
412
                    else
413
                    {
414
                        if(setMax)
415
                        {
416
                            min = max - (roundingUnit * units);
417
                        }
418
                        else if(setMin)
419
                        {
420
                            max = min + (roundingUnit * units);
421
                        }
422
                        else
423
                        {
424
                            min = this._roundDownToNearest(min, roundingUnit);
425
                            max = this._roundUpToNearest(max, roundingUnit);
426
                        }
427
                    }
428
                }
429
                else
430
                {
431
                    if(setMin)
432
                    {
433
                        if(alwaysShowZero)
434
                        {
435
                            max = 0;
436
                        }
437
                        else
438
                        {
439
                            max = min + (roundingUnit * units);
440
                        }
441
                    }
442
                    else if(!setMax)
443
                    {
444
                        if(alwaysShowZero || max === 0 || max + roundingUnit > 0)
445
                        {
446
                            max = 0;
447
                            roundingUnit = this._getMinimumUnit(max, min, units);
448
                            min = max - (roundingUnit * units);
449
                        }
450
                        else
451
                        {
452
                            min = this._roundDownToNearest(min, roundingUnit);
453
                            max = this._roundUpToNearest(max, roundingUnit);
454
                        }
455
                    }
456
                    else
457
                    {
458
                        min = max - (roundingUnit * units);
459
                    }
460
                }
461
            }
462
            else if(roundingMethod === "auto")
463
            {
464
                if(minGreaterThanZero && maxGreaterThanZero)
465
                {
466
                    if((alwaysShowZero || min < (max-min)/units) && !setMin)
467
                    {
468
                        min = 0;
469
                    }
470
 
471
                    roundingUnit = (max - min)/units;
472
                    if(useIntegers)
473
                    {
474
                        roundingUnit = Math.ceil(roundingUnit);
475
                        max = min + (roundingUnit * units);
476
                    }
477
                    else
478
                    {
479
                        max = min + Math.ceil(roundingUnit * units * 100000)/100000;
480
 
481
                    }
482
                }
483
                else if(maxGreaterThanZero && !minGreaterThanZero)
484
                {
485
                    if(alwaysShowZero)
486
                    {
487
                        topTicks = Math.round( units / ( (-1 * min) /max + 1) );
488
                        topTicks = Math.max(Math.min(topTicks, units - 1), 1);
489
                        botTicks = units - topTicks;
490
 
491
                        if(useIntegers)
492
                        {
493
                            tempMax = Math.ceil( max/topTicks );
494
                            tempMin = Math.floor( min/botTicks ) * -1;
495
                            roundingUnit = Math.max(tempMax, tempMin);
496
                            max = roundingUnit * topTicks;
497
                            min = roundingUnit * botTicks * -1;
498
                        }
499
                        else
500
                        {
501
                            tempMax = max/topTicks;
502
                            tempMin = min/botTicks * -1;
503
                            roundingUnit = Math.max(tempMax, tempMin);
504
                            max = Math.ceil(roundingUnit * topTicks * 100000)/100000;
505
                            min = Math.ceil(roundingUnit * botTicks * 100000)/100000 * -1;
506
                        }
507
                    }
508
                    else
509
                    {
510
                        roundingUnit = (max - min)/units;
511
                        if(useIntegers)
512
                        {
513
                            roundingUnit = Math.ceil(roundingUnit);
514
                        }
515
                        min = Math.round(this._roundDownToNearest(min, roundingUnit) * 100000)/100000;
516
                        max = Math.round(this._roundUpToNearest(max, roundingUnit) * 100000)/100000;
517
                    }
518
                }
519
                else
520
                {
521
                    roundingUnit = (max - min)/units;
522
                    if(useIntegers)
523
                    {
524
                        roundingUnit = Math.ceil(roundingUnit);
525
                    }
526
                    if(alwaysShowZero || max === 0 || max + roundingUnit > 0)
527
                    {
528
                        max = 0;
529
                        roundingUnit = (max - min)/units;
530
                        if(useIntegers)
531
                        {
532
                            Math.ceil(roundingUnit);
533
                            min = max - (roundingUnit * units);
534
                        }
535
                        else
536
                        {
537
                            min = max - Math.ceil(roundingUnit * units * 100000)/100000;
538
                        }
539
                    }
540
                    else
541
                    {
542
                        min = this._roundDownToNearest(min, roundingUnit);
543
                        max = this._roundUpToNearest(max, roundingUnit);
544
                    }
545
 
546
                }
547
            }
548
            else if(!isNaN(roundingMethod) && isFinite(roundingMethod))
549
            {
550
                roundingUnit = roundingMethod;
551
                minimumRange = roundingUnit * units;
552
                dataRangeGreater = (max - min) > minimumRange;
553
                minRound = this._roundDownToNearest(min, roundingUnit);
554
                maxRound = this._roundUpToNearest(max, roundingUnit);
555
                if(setMax)
556
                {
557
                    min = max - minimumRange;
558
                }
559
                else if(setMin)
560
                {
561
                    max = min + minimumRange;
562
                }
563
                else if(minGreaterThanZero && maxGreaterThanZero)
564
                {
565
                    if(alwaysShowZero || minRound <= 0)
566
                    {
567
                        min = 0;
568
                    }
569
                    else
570
                    {
571
                        min = minRound;
572
                    }
573
                    max = min + minimumRange;
574
                }
575
                else if(maxGreaterThanZero && !minGreaterThanZero)
576
                {
577
                    min = minRound;
578
                    max = maxRound;
579
                }
580
                else
581
                {
582
                    if(alwaysShowZero || maxRound >= 0)
583
                    {
584
                        max = 0;
585
                    }
586
                    else
587
                    {
588
                        max = maxRound;
589
                    }
590
                    min = max - minimumRange;
591
                }
592
            }
593
        }
594
        this._dataMaximum = max;
595
        this._dataMinimum = min;
596
    },
597
 
598
    /**
599
     * Rounds a Number to the nearest multiple of an input. For example, by rounding
600
     * 16 to the nearest 10, you will receive 20. Similar to the built-in function Math.round().
601
     *
602
     * @method _roundToNearest
603
     * @param {Number} number Number to round
604
     * @param {Number} nearest Multiple to round towards.
605
     * @return Number
606
     * @private
607
     */
608
    _roundToNearest: function(number, nearest)
609
    {
610
        nearest = nearest || 1;
611
        var roundedNumber = Math.round(this._roundToPrecision(number / nearest, 10)) * nearest;
612
        return this._roundToPrecision(roundedNumber, 10);
613
    },
614
 
615
    /**
616
     * Rounds a Number up to the nearest multiple of an input. For example, by rounding
617
     * 16 up to the nearest 10, you will receive 20. Similar to the built-in function Math.ceil().
618
     *
619
     * @method _roundUpToNearest
620
     * @param {Number} number Number to round
621
     * @param {Number} nearest Multiple to round towards.
622
     * @return Number
623
     * @private
624
     */
625
    _roundUpToNearest: function(number, nearest)
626
    {
627
        nearest = nearest || 1;
628
        return Math.ceil(this._roundToPrecision(number / nearest, 10)) * nearest;
629
    },
630
 
631
    /**
632
     * Rounds a Number down to the nearest multiple of an input. For example, by rounding
633
     * 16 down to the nearest 10, you will receive 10. Similar to the built-in function Math.floor().
634
     *
635
     * @method _roundDownToNearest
636
     * @param {Number} number Number to round
637
     * @param {Number} nearest Multiple to round towards.
638
     * @return Number
639
     * @private
640
     */
641
    _roundDownToNearest: function(number, nearest)
642
    {
643
        nearest = nearest || 1;
644
        return Math.floor(this._roundToPrecision(number / nearest, 10)) * nearest;
645
    },
646
 
647
    /**
648
     * Returns a coordinate corresponding to a data values.
649
     *
650
     * @method _getCoordFromValue
651
     * @param {Number} min The minimum for the axis.
652
     * @param {Number} max The maximum for the axis.
653
     * @param {Number} length The distance that the axis spans.
654
     * @param {Number} dataValue A value used to ascertain the coordinate.
655
     * @param {Number} offset Value in which to offset the coordinates.
656
     * @param {Boolean} reverse Indicates whether the coordinates should start from
657
     * the end of an axis. Only used in the numeric implementation.
658
     * @return Number
659
     * @private
660
     */
661
    _getCoordFromValue: function(min, max, length, dataValue, offset, reverse)
662
    {
663
        var range,
664
            multiplier,
665
            valuecoord,
666
            isNumber = Y_Lang.isNumber;
667
        dataValue = parseFloat(dataValue);
668
        if(isNumber(dataValue))
669
        {
670
            if(this.get("scaleType") === "logarithmic" && min > 0)
671
            {
672
                min = Math.log(min);
673
                max = Math.log(max);
674
                dataValue = Math.log(dataValue);
675
            }
676
            range = max - min;
677
            multiplier = length/range;
678
            valuecoord = (dataValue - min) * multiplier;
679
            valuecoord = reverse ? offset - valuecoord : offset + valuecoord;
680
        }
681
        else
682
        {
683
            valuecoord = NaN;
684
        }
685
        return valuecoord;
686
    },
687
 
688
    /**
689
     * Rounds a number to a certain level of precision. Useful for limiting the number of
690
     * decimal places on a fractional number.
691
     *
692
     * @method _roundToPrecision
693
     * @param {Number} number Number to round
694
     * @param {Number} precision Multiple to round towards.
695
     * @return Number
696
     * @private
697
     */
698
    _roundToPrecision: function(number, precision)
699
    {
700
        precision = precision || 0;
701
        var decimalPlaces = Math.pow(10, precision);
702
        return Math.round(decimalPlaces * number) / decimalPlaces;
703
    }
704
};
705
 
706
Y.NumericImpl = NumericImpl;
707
 
708
/**
709
 * NumericAxisBase manages numeric data for an axis.
710
 *
711
 * @class NumericAxisBase
712
 * @constructor
713
 * @extends AxisBase
714
 * @uses NumericImpl
715
 * @param {Object} config (optional) Configuration parameters.
716
 * @submodule axis-numeric-base
717
 */
718
Y.NumericAxisBase = Y.Base.create("numericAxisBase", Y.AxisBase, [Y.NumericImpl]);
719
 
720
 
721
}, '3.18.1', {"requires": ["axis-base"]});