|
1 YUI.add('series-stacked', function (Y, NAME) { |
|
2 |
|
3 /** |
|
4 * Provides functionality for creating stacked series. |
|
5 * |
|
6 * @module charts |
|
7 * @submodule series-stacked |
|
8 */ |
|
9 var Y_Lang = Y.Lang; |
|
10 |
|
11 /** |
|
12 * Utility class used for creating stacked series. |
|
13 * |
|
14 * @module charts |
|
15 * @class StackingUtil |
|
16 * @constructor |
|
17 * @submodule series-stacked |
|
18 */ |
|
19 function StackingUtil(){} |
|
20 |
|
21 StackingUtil.prototype = { |
|
22 /** |
|
23 * Indicates whether the series is stacked. |
|
24 * |
|
25 * @property _stacked |
|
26 * @private |
|
27 */ |
|
28 _stacked: true, |
|
29 |
|
30 /** |
|
31 * @protected |
|
32 * |
|
33 * Adjusts coordinate values for stacked series. |
|
34 * |
|
35 * @method _stackCoordinates |
|
36 */ |
|
37 _stackCoordinates: function() |
|
38 { |
|
39 if(this.get("direction") === "vertical") |
|
40 { |
|
41 this._stackXCoords(); |
|
42 } |
|
43 else |
|
44 { |
|
45 this._stackYCoords(); |
|
46 } |
|
47 }, |
|
48 |
|
49 /** |
|
50 * Stacks coordinates for a stacked vertical series. |
|
51 * |
|
52 * @method _stackXCoords |
|
53 * @protected |
|
54 */ |
|
55 _stackXCoords: function() |
|
56 { |
|
57 var order = this.get("order"), |
|
58 seriesCollection = this.get("seriesTypeCollection"), |
|
59 i = 0, |
|
60 xcoords = this.get("xcoords"), |
|
61 ycoords = this.get("ycoords"), |
|
62 len, |
|
63 coord, |
|
64 prevCoord, |
|
65 prevOrder, |
|
66 stackedXCoords = xcoords.concat(), |
|
67 prevXCoords, |
|
68 prevYCoords, |
|
69 nullIndices = [], |
|
70 nullIndex; |
|
71 if(order > 0) |
|
72 { |
|
73 prevXCoords = seriesCollection[order - 1].get("stackedXCoords"); |
|
74 prevYCoords = seriesCollection[order - 1].get("stackedYCoords"); |
|
75 len = prevXCoords.length; |
|
76 } |
|
77 else |
|
78 { |
|
79 len = xcoords.length; |
|
80 } |
|
81 for(; i < len; i = i + 1) |
|
82 { |
|
83 if(Y_Lang.isNumber(xcoords[i])) |
|
84 { |
|
85 if(order > 0) |
|
86 { |
|
87 prevCoord = prevXCoords[i]; |
|
88 if(!Y_Lang.isNumber(prevCoord)) |
|
89 { |
|
90 prevOrder = order; |
|
91 while(prevOrder > - 1 && !Y_Lang.isNumber(prevCoord)) |
|
92 { |
|
93 prevOrder = prevOrder - 1; |
|
94 if(prevOrder > -1) |
|
95 { |
|
96 prevCoord = seriesCollection[prevOrder].get("stackedXCoords")[i]; |
|
97 } |
|
98 else |
|
99 { |
|
100 prevCoord = this._leftOrigin; |
|
101 } |
|
102 } |
|
103 } |
|
104 xcoords[i] = xcoords[i] + prevCoord; |
|
105 } |
|
106 stackedXCoords[i] = xcoords[i]; |
|
107 } |
|
108 else |
|
109 { |
|
110 nullIndices.push(i); |
|
111 } |
|
112 } |
|
113 this._cleanXNaN(stackedXCoords, ycoords); |
|
114 len = nullIndices.length; |
|
115 if(len > 0) |
|
116 { |
|
117 for(i = 0; i < len; i = i + 1) |
|
118 { |
|
119 nullIndex = nullIndices[i]; |
|
120 coord = order > 0 ? prevXCoords[nullIndex] : this._leftOrigin; |
|
121 stackedXCoords[nullIndex] = Math.max(stackedXCoords[nullIndex], coord); |
|
122 } |
|
123 } |
|
124 this.set("stackedXCoords", stackedXCoords); |
|
125 this.set("stackedYCoords", ycoords); |
|
126 }, |
|
127 |
|
128 /** |
|
129 * Stacks coordinates for a stacked horizontal series. |
|
130 * |
|
131 * @method _stackYCoords |
|
132 * @protected |
|
133 */ |
|
134 _stackYCoords: function() |
|
135 { |
|
136 var order = this.get("order"), |
|
137 graphic = this.get("graphic"), |
|
138 h = graphic.get("height"), |
|
139 seriesCollection = this.get("seriesTypeCollection"), |
|
140 i = 0, |
|
141 xcoords = this.get("xcoords"), |
|
142 ycoords = this.get("ycoords"), |
|
143 len, |
|
144 coord, |
|
145 prevCoord, |
|
146 prevOrder, |
|
147 stackedYCoords = ycoords.concat(), |
|
148 prevXCoords, |
|
149 prevYCoords, |
|
150 nullIndices = [], |
|
151 nullIndex; |
|
152 if(order > 0) |
|
153 { |
|
154 prevXCoords = seriesCollection[order - 1].get("stackedXCoords"); |
|
155 prevYCoords = seriesCollection[order - 1].get("stackedYCoords"); |
|
156 len = prevYCoords.length; |
|
157 } |
|
158 else |
|
159 { |
|
160 len = ycoords.length; |
|
161 } |
|
162 for(; i < len; i = i + 1) |
|
163 { |
|
164 if(Y_Lang.isNumber(ycoords[i])) |
|
165 { |
|
166 if(order > 0) |
|
167 { |
|
168 prevCoord = prevYCoords[i]; |
|
169 if(!Y_Lang.isNumber(prevCoord)) |
|
170 { |
|
171 prevOrder = order; |
|
172 while(prevOrder > - 1 && !Y_Lang.isNumber(prevCoord)) |
|
173 { |
|
174 prevOrder = prevOrder - 1; |
|
175 if(prevOrder > -1) |
|
176 { |
|
177 prevCoord = seriesCollection[prevOrder].get("stackedYCoords")[i]; |
|
178 } |
|
179 else |
|
180 { |
|
181 prevCoord = this._bottomOrigin; |
|
182 } |
|
183 } |
|
184 } |
|
185 ycoords[i] = prevCoord - (h - ycoords[i]); |
|
186 } |
|
187 stackedYCoords[i] = ycoords[i]; |
|
188 } |
|
189 else |
|
190 { |
|
191 nullIndices.push(i); |
|
192 } |
|
193 } |
|
194 this._cleanYNaN(xcoords, stackedYCoords); |
|
195 len = nullIndices.length; |
|
196 if(len > 0) |
|
197 { |
|
198 for(i = 0; i < len; i = i + 1) |
|
199 { |
|
200 nullIndex = nullIndices[i]; |
|
201 coord = order > 0 ? prevYCoords[nullIndex] : h; |
|
202 stackedYCoords[nullIndex] = Math.min(stackedYCoords[nullIndex], coord); |
|
203 } |
|
204 } |
|
205 this.set("stackedXCoords", xcoords); |
|
206 this.set("stackedYCoords", stackedYCoords); |
|
207 }, |
|
208 |
|
209 /** |
|
210 * Cleans invalid x-coordinates by calculating their value based on the corresponding y-coordinate, the |
|
211 * previous valid x-coordinate with its corresponding y-coordinate and the next valid x-coordinate with |
|
212 * its corresponding y-coordinate. If there is no previous or next valid x-coordinate, the value will not |
|
213 * be altered. |
|
214 * |
|
215 * @method _cleanXNaN |
|
216 * @param {Array} xcoords An array of x-coordinate values |
|
217 * @param {Array} ycoords An arry of y-coordinate values |
|
218 * @private |
|
219 */ |
|
220 _cleanXNaN: function(xcoords, ycoords) |
|
221 { |
|
222 var previousValidIndex, |
|
223 nextValidIndex, |
|
224 previousValidX, |
|
225 previousValidY, |
|
226 x, |
|
227 y, |
|
228 nextValidX, |
|
229 nextValidY, |
|
230 isNumber = Y_Lang.isNumber, |
|
231 m, |
|
232 i = 0, |
|
233 len = ycoords.length; |
|
234 for(; i < len; ++i) |
|
235 { |
|
236 x = xcoords[i]; |
|
237 y = ycoords[i]; |
|
238 //if x is invalid, calculate where it should be |
|
239 if(!isNumber(x) && i > 0 && i < len - 1) |
|
240 { |
|
241 previousValidY = ycoords[i - 1]; |
|
242 //check to see if the previous value is valid |
|
243 previousValidX = this._getPreviousValidCoordValue(xcoords, i); |
|
244 nextValidY = ycoords[i + 1]; |
|
245 nextValidX = this._getNextValidCoordValue(xcoords, i); |
|
246 //check to see if the next value is valid |
|
247 if(isNumber(previousValidX) && isNumber(nextValidX)) |
|
248 { |
|
249 //calculate slope and solve for x |
|
250 m = (nextValidY - previousValidY) / (nextValidX - previousValidX); |
|
251 xcoords[i] = (y + (m * previousValidX) - previousValidY)/m; |
|
252 } |
|
253 previousValidIndex = NaN; |
|
254 nextValidIndex = NaN; |
|
255 } |
|
256 } |
|
257 }, |
|
258 |
|
259 /** |
|
260 * Returns the previous valid (numeric) value in an array if available. |
|
261 * |
|
262 * @method _getPreviousValidCoordValue |
|
263 * @param {Array} coords Array of values |
|
264 * @param {Number} index The index in the array in which to begin searching. |
|
265 * @return Number |
|
266 * @private |
|
267 */ |
|
268 _getPreviousValidCoordValue: function(coords, index) |
|
269 { |
|
270 var coord, |
|
271 isNumber = Y_Lang.isNumber, |
|
272 limit = -1; |
|
273 while(!isNumber(coord) && index > limit) |
|
274 { |
|
275 index = index - 1; |
|
276 coord = coords[index]; |
|
277 } |
|
278 return coord; |
|
279 }, |
|
280 |
|
281 /** |
|
282 * Returns the next valid (numeric) value in an array if available. |
|
283 * |
|
284 * @method _getNextValidCoordValue |
|
285 * @param {Array} coords Array of values |
|
286 * @param {Number} index The index in the array in which to begin searching. |
|
287 * @return Number |
|
288 * @private |
|
289 */ |
|
290 _getNextValidCoordValue: function(coords, index) |
|
291 { |
|
292 var coord, |
|
293 isNumber = Y_Lang.isNumber, |
|
294 limit = coords.length; |
|
295 while(!isNumber(coord) && index < limit) |
|
296 { |
|
297 index = index + 1; |
|
298 coord = coords[index]; |
|
299 } |
|
300 return coord; |
|
301 }, |
|
302 |
|
303 /** |
|
304 * Cleans invalid y-coordinates by calculating their value based on the corresponding x-coordinate, the |
|
305 * previous valid y-coordinate with its corresponding x-coordinate and the next valid y-coordinate with |
|
306 * its corresponding x-coordinate. If there is no previous or next valid y-coordinate, the value will not |
|
307 * be altered. |
|
308 * |
|
309 * @method _cleanYNaN |
|
310 * @param {Array} xcoords An array of x-coordinate values |
|
311 * @param {Array} ycoords An arry of y-coordinate values |
|
312 * @private |
|
313 */ |
|
314 _cleanYNaN: function(xcoords, ycoords) |
|
315 { |
|
316 var previousValidIndex, |
|
317 nextValidIndex, |
|
318 previousValidX, |
|
319 previousValidY, |
|
320 x, |
|
321 y, |
|
322 nextValidX, |
|
323 nextValidY, |
|
324 isNumber = Y_Lang.isNumber, |
|
325 m, |
|
326 i = 0, |
|
327 len = xcoords.length; |
|
328 for(; i < len; ++i) |
|
329 { |
|
330 x = xcoords[i]; |
|
331 y = ycoords[i]; |
|
332 //if y is invalid, calculate where it should be |
|
333 if(!isNumber(y) && i > 0 && i < len - 1) |
|
334 { |
|
335 //check to see if the previous value is valid |
|
336 previousValidX = xcoords[i - 1]; |
|
337 previousValidY = this._getPreviousValidCoordValue(ycoords, i); |
|
338 //check to see if the next value is valid |
|
339 nextValidX = xcoords[i + 1]; |
|
340 nextValidY = this._getNextValidCoordValue(ycoords, i); |
|
341 if(isNumber(previousValidY) && isNumber(nextValidY)) |
|
342 { |
|
343 //calculate slope and solve for y |
|
344 m = (nextValidY - previousValidY) / (nextValidX - previousValidX); |
|
345 ycoords[i] = previousValidY + ((m * x) - (m * previousValidX)); |
|
346 } |
|
347 previousValidIndex = NaN; |
|
348 nextValidIndex = NaN; |
|
349 } |
|
350 } |
|
351 } |
|
352 }; |
|
353 Y.StackingUtil = StackingUtil; |
|
354 |
|
355 |
|
356 }, '@VERSION@', {"requires": ["axis-stacked"]}); |