src/cm/media/js/lib/yui/yui_3.10.3/build/series-fill-util/series-fill-util.js
changeset 525 89ef5ed3c48b
equal deleted inserted replaced
524:322d0feea350 525:89ef5ed3c48b
       
     1 /*
       
     2 YUI 3.10.3 (build 2fb5187)
       
     3 Copyright 2013 Yahoo! Inc. All rights reserved.
       
     4 Licensed under the BSD License.
       
     5 http://yuilibrary.com/license/
       
     6 */
       
     7 
       
     8 YUI.add('series-fill-util', function (Y, NAME) {
       
     9 
       
    10 /**
       
    11  * Provides functionality for drawing fills in a series.
       
    12  *
       
    13  * @module charts
       
    14  * @submodule series-fill-util
       
    15  */
       
    16 var Y_Lang = Y.Lang;
       
    17 
       
    18 /**
       
    19  * Utility class used for drawing area fills.
       
    20  *
       
    21  * @class Fills
       
    22  * @constructor
       
    23  * @submodule series-fill-util
       
    24  */
       
    25 function Fills() {}
       
    26 
       
    27 Fills.ATTRS = {
       
    28     area: {
       
    29         getter: function()
       
    30         {
       
    31             return this._defaults || this._getAreaDefaults();
       
    32         },
       
    33 
       
    34         setter: function(val)
       
    35         {
       
    36             var defaults = this._defaults || this._getAreaDefaults();
       
    37             this._defaults = Y.merge(defaults, val);
       
    38         }
       
    39     }
       
    40 };
       
    41 
       
    42 Fills.prototype = {
       
    43     /**
       
    44      * Returns a path shape used for drawing fills.
       
    45      *
       
    46      * @method _getPath
       
    47      * @return Path
       
    48      * @private
       
    49      */
       
    50     _getPath: function()
       
    51     {
       
    52         var path = this._path;
       
    53         if(!path)
       
    54         {
       
    55             path = this.get("graphic").addShape({type:"path"});
       
    56             this._path = path;
       
    57         }
       
    58         return path;
       
    59     },
       
    60 
       
    61     /**
       
    62      * Toggles visibility
       
    63      *
       
    64      * @method _toggleVisible
       
    65      * @param {Boolean} visible indicates visibilitye
       
    66      * @private
       
    67      */
       
    68     _toggleVisible: function(visible)
       
    69     {
       
    70         if(this._path)
       
    71         {
       
    72             this._path.set("visible", visible);
       
    73         }
       
    74     },
       
    75 
       
    76     /**
       
    77      * Draws fill
       
    78      *
       
    79      * @method drawFill
       
    80      * @param {Array} xcoords The x-coordinates for the series.
       
    81      * @param {Array} ycoords The y-coordinates for the series.
       
    82      * @protected
       
    83      */
       
    84     drawFill: function(xcoords, ycoords)
       
    85     {
       
    86         if(xcoords.length < 1)
       
    87         {
       
    88             return;
       
    89         }
       
    90         var isNumber = Y_Lang.isNumber,
       
    91             len = xcoords.length,
       
    92             firstX = xcoords[0],
       
    93             firstY = ycoords[0],
       
    94             lastValidX = firstX,
       
    95             lastValidY = firstY,
       
    96             nextX,
       
    97             nextY,
       
    98             pointValid,
       
    99             noPointsRendered = true,
       
   100             i = 0,
       
   101             styles = this.get("styles").area,
       
   102             path = this._getPath(),
       
   103             color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
       
   104         path.clear();
       
   105         path.set("fill", {
       
   106             color: color,
       
   107             opacity: styles.alpha
       
   108         });
       
   109         path.set("stroke", {weight: 0});
       
   110         for(; i < len; i = ++i)
       
   111         {
       
   112             nextX = xcoords[i];
       
   113             nextY = ycoords[i];
       
   114             pointValid = isNumber(nextX) && isNumber(nextY);
       
   115             if(!pointValid)
       
   116             {
       
   117                 continue;
       
   118             }
       
   119             if(noPointsRendered)
       
   120             {
       
   121                 this._firstValidX = nextX;
       
   122                 this._firstValidY = nextY;
       
   123                 noPointsRendered = false;
       
   124                 path.moveTo(nextX, nextY);
       
   125             }
       
   126             else
       
   127             {
       
   128                 path.lineTo(nextX, nextY);
       
   129             }
       
   130             lastValidX = nextX;
       
   131             lastValidY = nextY;
       
   132         }
       
   133         this._lastValidX = lastValidX;
       
   134         this._lastValidY = lastValidY;
       
   135         path.end();
       
   136     },
       
   137 
       
   138     /**
       
   139      * Draws a fill for a spline
       
   140      *
       
   141      * @method drawAreaSpline
       
   142      * @protected
       
   143      */
       
   144     drawAreaSpline: function()
       
   145     {
       
   146         if(this.get("xcoords").length < 1)
       
   147         {
       
   148             return;
       
   149         }
       
   150         var xcoords = this.get("xcoords"),
       
   151             ycoords = this.get("ycoords"),
       
   152             curvecoords = this.getCurveControlPoints(xcoords, ycoords),
       
   153             len = curvecoords.length,
       
   154             cx1,
       
   155             cx2,
       
   156             cy1,
       
   157             cy2,
       
   158             x,
       
   159             y,
       
   160             i = 0,
       
   161             firstX = xcoords[0],
       
   162             firstY = ycoords[0],
       
   163             styles = this.get("styles").area,
       
   164             path = this._getPath(),
       
   165             color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
       
   166         path.set("fill", {
       
   167             color: color,
       
   168             opacity: styles.alpha
       
   169         });
       
   170         path.set("stroke", {weight: 0});
       
   171         path.moveTo(firstX, firstY);
       
   172         for(; i < len; i = ++i)
       
   173         {
       
   174             x = curvecoords[i].endx;
       
   175             y = curvecoords[i].endy;
       
   176             cx1 = curvecoords[i].ctrlx1;
       
   177             cx2 = curvecoords[i].ctrlx2;
       
   178             cy1 = curvecoords[i].ctrly1;
       
   179             cy2 = curvecoords[i].ctrly2;
       
   180             path.curveTo(cx1, cy1, cx2, cy2, x, y);
       
   181         }
       
   182         if(this.get("direction") === "vertical")
       
   183         {
       
   184             path.lineTo(this._leftOrigin, y);
       
   185             path.lineTo(this._leftOrigin, firstY);
       
   186         }
       
   187         else
       
   188         {
       
   189             path.lineTo(x, this._bottomOrigin);
       
   190             path.lineTo(firstX, this._bottomOrigin);
       
   191         }
       
   192         path.lineTo(firstX, firstY);
       
   193         path.end();
       
   194     },
       
   195 
       
   196     /**
       
   197      * Draws a a stacked area spline
       
   198      *
       
   199      * @method drawStackedAreaSpline
       
   200      * @protected
       
   201      */
       
   202     drawStackedAreaSpline: function()
       
   203     {
       
   204         if(this.get("xcoords").length < 1)
       
   205         {
       
   206             return;
       
   207         }
       
   208         var xcoords = this.get("xcoords"),
       
   209             ycoords = this.get("ycoords"),
       
   210             curvecoords,
       
   211             order = this.get("order"),
       
   212             seriesCollection = this.get("seriesTypeCollection"),
       
   213             prevXCoords,
       
   214             prevYCoords,
       
   215             len,
       
   216             cx1,
       
   217             cx2,
       
   218             cy1,
       
   219             cy2,
       
   220             x,
       
   221             y,
       
   222             i = 0,
       
   223             firstX,
       
   224             firstY,
       
   225             styles = this.get("styles").area,
       
   226             path = this._getPath(),
       
   227             color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
       
   228         firstX = xcoords[0];
       
   229         firstY = ycoords[0];
       
   230         curvecoords = this.getCurveControlPoints(xcoords, ycoords);
       
   231         len = curvecoords.length;
       
   232         path.set("fill", {
       
   233             color: color,
       
   234             opacity: styles.alpha
       
   235         });
       
   236         path.set("stroke", {weight: 0});
       
   237         path.moveTo(firstX, firstY);
       
   238         for(; i < len; i = ++i)
       
   239         {
       
   240             x = curvecoords[i].endx;
       
   241             y = curvecoords[i].endy;
       
   242             cx1 = curvecoords[i].ctrlx1;
       
   243             cx2 = curvecoords[i].ctrlx2;
       
   244             cy1 = curvecoords[i].ctrly1;
       
   245             cy2 = curvecoords[i].ctrly2;
       
   246             path.curveTo(cx1, cy1, cx2, cy2, x, y);
       
   247         }
       
   248         if(order > 0)
       
   249         {
       
   250             prevXCoords = seriesCollection[order - 1].get("xcoords").concat().reverse();
       
   251             prevYCoords = seriesCollection[order - 1].get("ycoords").concat().reverse();
       
   252             curvecoords = this.getCurveControlPoints(prevXCoords, prevYCoords);
       
   253             i = 0;
       
   254             len = curvecoords.length;
       
   255             path.lineTo(prevXCoords[0], prevYCoords[0]);
       
   256             for(; i < len; i = ++i)
       
   257             {
       
   258                 x = curvecoords[i].endx;
       
   259                 y = curvecoords[i].endy;
       
   260                 cx1 = curvecoords[i].ctrlx1;
       
   261                 cx2 = curvecoords[i].ctrlx2;
       
   262                 cy1 = curvecoords[i].ctrly1;
       
   263                 cy2 = curvecoords[i].ctrly2;
       
   264                 path.curveTo(cx1, cy1, cx2, cy2, x, y);
       
   265             }
       
   266         }
       
   267         else
       
   268         {
       
   269             if(this.get("direction") === "vertical")
       
   270             {
       
   271                 path.lineTo(this._leftOrigin, ycoords[ycoords.length-1]);
       
   272                 path.lineTo(this._leftOrigin, firstY);
       
   273             }
       
   274             else
       
   275             {
       
   276                 path.lineTo(xcoords[xcoords.length-1], this._bottomOrigin);
       
   277                 path.lineTo(firstX, this._bottomOrigin);
       
   278             }
       
   279 
       
   280         }
       
   281         path.lineTo(firstX, firstY);
       
   282         path.end();
       
   283     },
       
   284 
       
   285     /**
       
   286      * Storage for default area styles.
       
   287      *
       
   288      * @property _defaults
       
   289      * @type Object
       
   290      * @private
       
   291      */
       
   292     _defaults: null,
       
   293 
       
   294     /**
       
   295      * Concatenates coordinate array with correct coordinates for closing an area fill.
       
   296      *
       
   297      * @method _getClosingPoints
       
   298      * @return Array
       
   299      * @protected
       
   300      */
       
   301     _getClosingPoints: function()
       
   302     {
       
   303         var xcoords = this.get("xcoords").concat(),
       
   304             ycoords = this.get("ycoords").concat(),
       
   305             firstValidIndex,
       
   306             lastValidIndex;
       
   307         if(this.get("direction") === "vertical")
       
   308         {
       
   309             lastValidIndex = this._getLastValidIndex(xcoords);
       
   310             firstValidIndex = this._getFirstValidIndex(xcoords);
       
   311             ycoords.push(ycoords[lastValidIndex]);
       
   312             ycoords.push(ycoords[firstValidIndex]);
       
   313             xcoords.push(this._leftOrigin);
       
   314             xcoords.push(this._leftOrigin);
       
   315         }
       
   316         else
       
   317         {
       
   318             lastValidIndex = this._getLastValidIndex(ycoords);
       
   319             firstValidIndex = this._getFirstValidIndex(ycoords);
       
   320             xcoords.push(xcoords[lastValidIndex]);
       
   321             xcoords.push(xcoords[firstValidIndex]);
       
   322             ycoords.push(this._bottomOrigin);
       
   323             ycoords.push(this._bottomOrigin);
       
   324         }
       
   325         xcoords.push(xcoords[0]);
       
   326         ycoords.push(ycoords[0]);
       
   327         return [xcoords, ycoords];
       
   328     },
       
   329 
       
   330     /**
       
   331      * Returns the order of the series closest to the current series that has a valid value for the current index.
       
   332      *
       
   333      * @method _getHighestValidOrder
       
   334      * @param {Array} seriesCollection Array of series of a given type.
       
   335      * @param {Number} index Index of the series item.
       
   336      * @param {Number} order Index of the the series in the seriesCollection
       
   337      * @param {String} direction Indicates the direction of the series
       
   338      * @return Number
       
   339      * @private
       
   340      */
       
   341     _getHighestValidOrder: function(seriesCollection, index, order, direction)
       
   342     {
       
   343         var coords = direction === "vertical" ? "stackedXCoords" : "stackedYCoords",
       
   344             coord;
       
   345         while(isNaN(coord) && order > -1)
       
   346         {
       
   347           order = order - 1;
       
   348           if(order > -1)
       
   349           {
       
   350             coord = seriesCollection[order].get(coords)[index];
       
   351           }
       
   352         }
       
   353         return order;
       
   354     },
       
   355 
       
   356     /**
       
   357      * Returns an array containing the x and y coordinates for a given series and index.
       
   358      *
       
   359      * @method _getCoordsByOrderAndIndex
       
   360      * @param {Array} seriesCollection Array of series of a given type.
       
   361      * @param {Number} index Index of the series item.
       
   362      * @param {Number} order Index of the the series in the seriesCollection
       
   363      * @param {String} direction Indicates the direction of the series
       
   364      * @return Array
       
   365      * @private
       
   366      */
       
   367     _getCoordsByOrderAndIndex: function(seriesCollection, index, order, direction)
       
   368     {
       
   369         var xcoord,
       
   370             ycoord;
       
   371         if(direction === "vertical")
       
   372         {
       
   373             xcoord = order < 0 ? this._leftOrigin : seriesCollection[order].get("stackedXCoords")[index];
       
   374             ycoord = this.get("stackedYCoords")[index];
       
   375         }
       
   376         else
       
   377         {
       
   378             xcoord = this.get("stackedXCoords")[index];
       
   379             ycoord = order < 0 ? this._bottomOrigin : seriesCollection[order].get("stackedYCoords")[index];
       
   380         }
       
   381         return [xcoord, ycoord];
       
   382     },
       
   383 
       
   384     /**
       
   385      * Concatenates coordinate array with the correct coordinates for closing an area stack.
       
   386      *
       
   387      * @method _getStackedClosingPoints
       
   388      * @return Array
       
   389      * @protected
       
   390      */
       
   391     _getStackedClosingPoints: function()
       
   392     {
       
   393         var order = this.get("order"),
       
   394             direction = this.get("direction"),
       
   395             seriesCollection = this.get("seriesTypeCollection"),
       
   396             firstValidIndex,
       
   397             lastValidIndex,
       
   398             xcoords = this.get("stackedXCoords"),
       
   399             ycoords = this.get("stackedYCoords"),
       
   400             limit,
       
   401             previousSeries,
       
   402             previousSeriesFirstValidIndex,
       
   403             previousSeriesLastValidIndex,
       
   404             previousXCoords,
       
   405             previousYCoords,
       
   406             coords,
       
   407             closingXCoords,
       
   408             closingYCoords,
       
   409             currentIndex,
       
   410             highestValidOrder,
       
   411             oldOrder;
       
   412         if(order < 1)
       
   413         {
       
   414           return this._getClosingPoints();
       
   415         }
       
   416 
       
   417         previousSeries = seriesCollection[order - 1];
       
   418         previousXCoords = previousSeries.get("stackedXCoords").concat();
       
   419         previousYCoords = previousSeries.get("stackedYCoords").concat();
       
   420         if(direction === "vertical")
       
   421         {
       
   422             firstValidIndex = this._getFirstValidIndex(xcoords);
       
   423             lastValidIndex = this._getLastValidIndex(xcoords);
       
   424             previousSeriesFirstValidIndex = previousSeries._getFirstValidIndex(previousXCoords);
       
   425             previousSeriesLastValidIndex = previousSeries._getLastValidIndex(previousXCoords);
       
   426         }
       
   427         else
       
   428         {
       
   429             firstValidIndex = this._getFirstValidIndex(ycoords);
       
   430             lastValidIndex = this._getLastValidIndex(ycoords);
       
   431             previousSeriesFirstValidIndex = previousSeries._getFirstValidIndex(previousYCoords);
       
   432             previousSeriesLastValidIndex = previousSeries._getLastValidIndex(previousYCoords);
       
   433         }
       
   434         if(previousSeriesLastValidIndex >= firstValidIndex && previousSeriesFirstValidIndex <= lastValidIndex)
       
   435         {
       
   436             previousSeriesFirstValidIndex = Math.max(firstValidIndex, previousSeriesFirstValidIndex);
       
   437             previousSeriesLastValidIndex = Math.min(lastValidIndex, previousSeriesLastValidIndex);
       
   438             previousXCoords = previousXCoords.slice(previousSeriesFirstValidIndex, previousSeriesLastValidIndex + 1);
       
   439             previousYCoords = previousYCoords.slice(previousSeriesFirstValidIndex, previousSeriesLastValidIndex + 1);
       
   440             limit = previousSeriesFirstValidIndex;
       
   441         }
       
   442         else
       
   443         {
       
   444             limit = lastValidIndex;
       
   445         }
       
   446 
       
   447         closingXCoords = [xcoords[firstValidIndex]];
       
   448         closingYCoords = [ycoords[firstValidIndex]];
       
   449         currentIndex = firstValidIndex;
       
   450         while((isNaN(highestValidOrder) || highestValidOrder < order - 1) && currentIndex <= limit)
       
   451         {
       
   452             oldOrder = highestValidOrder;
       
   453             highestValidOrder = this._getHighestValidOrder(seriesCollection, currentIndex, order, direction);
       
   454             if(!isNaN(oldOrder) && highestValidOrder > oldOrder)
       
   455             {
       
   456                 coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex, oldOrder, direction);
       
   457                 closingXCoords.push(coords[0]);
       
   458                 closingYCoords.push(coords[1]);
       
   459             }
       
   460             coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex, highestValidOrder, direction);
       
   461             closingXCoords.push(coords[0]);
       
   462             closingYCoords.push(coords[1]);
       
   463             currentIndex = currentIndex + 1;
       
   464         }
       
   465         if(previousXCoords &&
       
   466             previousXCoords.length > 0 &&
       
   467             previousSeriesLastValidIndex > firstValidIndex &&
       
   468             previousSeriesFirstValidIndex < lastValidIndex)
       
   469         {
       
   470             closingXCoords = closingXCoords.concat(previousXCoords);
       
   471             closingYCoords = closingYCoords.concat(previousYCoords);
       
   472             highestValidOrder = order -1;
       
   473         }
       
   474         currentIndex = Math.max(firstValidIndex, previousSeriesLastValidIndex);
       
   475         order = order - 1;
       
   476         highestValidOrder = NaN;
       
   477         while(currentIndex <= lastValidIndex)
       
   478         {
       
   479             oldOrder = highestValidOrder;
       
   480             highestValidOrder = this._getHighestValidOrder(seriesCollection, currentIndex, order, direction);
       
   481             if(!isNaN(oldOrder))
       
   482             {
       
   483                 if(highestValidOrder > oldOrder)
       
   484                 {
       
   485                     coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex, oldOrder, direction);
       
   486                     closingXCoords.push(coords[0]);
       
   487                     closingYCoords.push(coords[1]);
       
   488                 }
       
   489                 else if(highestValidOrder < oldOrder)
       
   490                 {
       
   491                     coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex - 1, highestValidOrder, direction);
       
   492                     closingXCoords.push(coords[0]);
       
   493                     closingYCoords.push(coords[1]);
       
   494                 }
       
   495             }
       
   496             coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex, highestValidOrder, direction);
       
   497             closingXCoords.push(coords[0]);
       
   498             closingYCoords.push(coords[1]);
       
   499             currentIndex = currentIndex + 1;
       
   500         }
       
   501 
       
   502         closingXCoords.reverse();
       
   503         closingYCoords.reverse();
       
   504         return [xcoords.concat(closingXCoords), ycoords.concat(closingYCoords)];
       
   505     },
       
   506 
       
   507     /**
       
   508      * Returns default values for area styles.
       
   509      *
       
   510      * @method _getAreaDefaults
       
   511      * @return Object
       
   512      * @private
       
   513      */
       
   514     _getAreaDefaults: function()
       
   515     {
       
   516         return {
       
   517         };
       
   518     }
       
   519 };
       
   520 Y.augment(Fills, Y.Attribute);
       
   521 Y.Fills = Fills;
       
   522 
       
   523 
       
   524 }, '3.10.3');