1/* * 2 * 3 * (c) 2010-2024 Torstein Honsi 4 * 5 * License: www.highcharts.com/license 6 * 7 * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! 8 * 9 * */ 10'use strict'; 11import AreaSeriesDefaults from './AreaSeriesDefaults.js'; 12import SeriesRegistry from '../../Core/Series/SeriesRegistry.js'; 13const { seriesTypes: { line: LineSeries } } = SeriesRegistry; 14import U from '../../Core/Utilities.js'; 15const { extend, merge, objectEach, pick } = U; 16/* * 17 * 18 * Class 19 * 20 * */ 21/** 22 * Area series type. 23 * 24 * @private 25 * @class 26 * @name AreaSeries 27 * 28 * @augments LineSeries 29 */ 30class AreaSeries extends LineSeries { 31 /* * 32 * 33 * Functions 34 * 35 * */ 36 /* eslint-disable valid-jsdoc */ 37 /** 38 * Draw the graph and the underlying area. This method calls the Series 39 * base function and adds the area. The areaPath is calculated in the 40 * getSegmentPath method called from Series.prototype.drawGraph. 41 * @private 42 */ 43 drawGraph() { 44 // Define or reset areaPath 45 this.areaPath = []; 46 // Call the base method 47 super.drawGraph.apply(this); 48 // Define local variables 49 const { areaPath, options } = this; 50 [this, ...this.zones].forEach((owner, i) => { 51 const attribs = {}, fillColor = owner.fillColor || options.fillColor; 52 let area = owner.area; 53 const verb = area ? 'animate' : 'attr'; 54 // Create or update the area 55 if (area) { // Update 56 area.endX = this.preventGraphAnimation ? 57 null : 58 areaPath.xMap; 59 area.animate({ d: areaPath }); 60 } 61 else { // Create 62 attribs.zIndex = 0; // #1069 63 /** 64 * SVG element of area-based charts. Can be used for styling 65 * purposes. If zones are configured, this element will be 66 * hidden and replaced by multiple zone areas, accessible 67 * via `series.zones[i].area`. 68 * 69 * @name Highcharts.Series#area 70 * @type {Highcharts.SVGElement|undefined} 71 */ 72 area = owner.area = this.chart.renderer 73 .path(areaPath) 74 .addClass('highcharts-area' + 75 (i ? ` highcharts-zone-area-${i - 1} ` : ' ') + 76 ((i && owner.className) || '')) 77 .add(this.group); 78 area.isArea = true; 79 } 80 if (!this.chart.styledMode) { 81 // If there is fillColor defined for the area, set it. 82 // Otherwise, we set it to the zone/series color and add 83 // fill-opacity (#18939). 84 attribs.fill = fillColor || owner.color || this.color; 85 attribs['fill-opacity'] = fillColor ? 86 1 : (options.fillOpacity ?? 0.75); 87 // Allow clicking through the area if sticky tracking is true 88 // (#18744) 89 area.css({ 90 pointerEvents: this.stickyTracking ? 'none' : 'auto' 91 }); 92 } 93 area[verb](attribs); 94 area.startX = areaPath.xMap; 95 area.shiftUnit = options.step ? 2 : 1; 96 }); 97 } 98 /** 99 * @private 100 */ 101 getGraphPath(points) { 102 const getGraphPath = LineSeries.prototype.getGraphPath, options = this.options, stacking = options.stacking, yAxis = this.yAxis, bottomPoints = [], graphPoints = [], seriesIndex = this.index, stacks = yAxis.stacking.stacks[this.stackKey], threshold = options.threshold, translatedThreshold = Math.round(// #10909 103 yAxis.getThreshold(options.threshold)), connectNulls = pick(// #10574 104 options.connectNulls, stacking === 'percent'), 105 // To display null points in underlying stacked series, this 106 // series graph must be broken, and the area also fall down to 107 // fill the gap left by the null point. #2069 108 addDummyPoints = function (i, otherI, side) { 109 const point = points[i], stackedValues = stacking && 110 stacks[point.x].points[seriesIndex], nullVal = point[side + 'Null'] || 0, cliffVal = point[side + 'Cliff'] || 0; 111 let top, bottom, isNull = true; 112 if (cliffVal || nullVal) { 113 top = (nullVal ? 114 stackedValues[0] : 115 stackedValues[1]) + cliffVal; 116 bottom = stackedValues[0] + cliffVal; 117 isNull = !!nullVal; 118 } 119 else if (!stacking && 120 points[otherI] && 121 points[otherI].isNull) { 122 top = bottom = threshold; 123 } 124 // Add to the top and bottom line of the area 125 if (typeof top !== 'undefined') { 126 graphPoints.push({ 127 plotX: plotX, 128 plotY: top === null ? 129 translatedThreshold : 130 yAxis.getThreshold(top), 131 isNull: isNull, 132 isCliff: true 133 }); 134 bottomPoints.push({ 135 plotX: plotX, 136 plotY: bottom === null ? 137 translatedThreshold : 138 yAxis.getThreshold(bottom), 139 doCurve: false // #1041, gaps in areaspline areas 140 }); 141 } 142 }; 143 let plotX, isNull, yBottom; 144 // Find what points to use 145 points = points || this.points; 146 // Fill in missing points 147 if (stacking) {
vendor: 8,085 bytes, lines 148-325
148 points = this.getStackPoints(points); 149 } 150 for (let i = 0, iEnd = points.length; i < iEnd; ++i) { 151 // Reset after series.update of stacking property (#12033) 152 if (!stacking) { 153 points[i].leftCliff = points[i].rightCliff = 154 points[i].leftNull = points[i].rightNull = void 0; 155 } 156 isNull = points[i].isNull; 157 plotX = pick(points[i].rectPlotX, points[i].plotX); 158 yBottom = stacking ? 159 pick(points[i].yBottom, translatedThreshold) : 160 translatedThreshold; 161 if (!isNull || connectNulls) { 162 if (!connectNulls) { 163 addDummyPoints(i, i - 1, 'left'); 164 } 165 // Skip null point when stacking is false and connectNulls 166 // true 167 if (!(isNull && !stacking && connectNulls)) { 168 graphPoints.push(points[i]); 169 bottomPoints.push({ 170 x: i, 171 plotX: plotX, 172 plotY: yBottom 173 }); 174 } 175 if (!connectNulls) { 176 addDummyPoints(i, i + 1, 'right'); 177 } 178 } 179 } 180 const topPath = getGraphPath.call(this, graphPoints, true, true); 181 bottomPoints.reversed = true; 182 const bottomPath = getGraphPath.call(this, bottomPoints, true, true); 183 const firstBottomPoint = bottomPath[0]; 184 if (firstBottomPoint && firstBottomPoint[0] === 'M') { 185 bottomPath[0] = ['L', firstBottomPoint[1], firstBottomPoint[2]]; 186 } 187 const areaPath = topPath.concat(bottomPath); 188 if (areaPath.length) { 189 areaPath.push(['Z']); 190 } 191 // TODO: don't set leftCliff and rightCliff when connectNulls? 192 const graphPath = getGraphPath 193 .call(this, graphPoints, false, connectNulls); 194 if (this.chart.series.length > 1 && 195 stacking && 196 graphPoints.some((point) => point.isCliff)) { 197 areaPath.hasStackedCliffs = graphPath.hasStackedCliffs = true; 198 } 199 areaPath.xMap = topPath.xMap; 200 this.areaPath = areaPath; 201 return graphPath; 202 } 203 /** 204 * Return an array of stacked points, where null and missing points are 205 * replaced by dummy points in order for gaps to be drawn correctly in 206 * stacks. 207 * @private 208 */ 209 getStackPoints(points) { 210 const series = this, segment = [], keys = [], xAxis = this.xAxis, yAxis = this.yAxis, stack = yAxis.stacking.stacks[this.stackKey], pointMap = {}, yAxisSeries = yAxis.series, seriesLength = yAxisSeries.length, upOrDown = yAxis.options.reversedStacks ? 1 : -1, seriesIndex = yAxisSeries.indexOf(series); 211 points = points || this.points; 212 if (this.options.stacking) { 213 for (let i = 0; i < points.length; i++) { 214 // Reset after point update (#7326) 215 points[i].leftNull = points[i].rightNull = void 0; 216 // Create a map where we can quickly look up the points by 217 // their X values. 218 pointMap[points[i].x] = points[i]; 219 } 220 // Sort the keys (#1651) 221 objectEach(stack, function (stackX, x) { 222 // Nulled after switching between 223 // grouping and not (#1651, #2336) 224 if (stackX.total !== null) { 225 keys.push(x); 226 } 227 }); 228 keys.sort(function (a, b) { 229 return a - b; 230 }); 231 const visibleSeries = yAxisSeries.map((s) => s.visible); 232 keys.forEach(function (x, idx) { 233 let y = 0, stackPoint, stackedValues; 234 if (pointMap[x] && !pointMap[x].isNull) { 235 segment.push(pointMap[x]); 236 // Find left and right cliff. -1 goes left, 1 goes 237 // right. 238 [-1, 1].forEach(function (direction) { 239 const nullName = direction === 1 ? 240 'rightNull' : 241 'leftNull', cliffName = direction === 1 ? 242 'rightCliff' : 243 'leftCliff', otherStack = stack[keys[idx + direction]]; 244 let cliff = 0; 245 // If there is a stack next to this one, 246 // to the left or to the right... 247 if (otherStack) { 248 let i = seriesIndex; 249 // Can go either up or down, 250 // depending on reversedStacks 251 while (i >= 0 && i < seriesLength) { 252 const si = yAxisSeries[i].index; 253 stackPoint = otherStack.points[si]; 254 if (!stackPoint) { 255 // If the next point in this series is 256 // missing, mark the point with 257 // point.leftNull or point.rightNull = true. 258 if (si === series.index) { 259 pointMap[x][nullName] = true; 260 // If there are missing points in the next 261 // stack in any of the series below this 262 // one, we need to subtract the missing 263 // values and add a hiatus to the left or 264 // right. 265 } 266 else if (visibleSeries[i]) { 267 stackedValues = stack[x].points[si]; 268 if (stackedValues) { 269 cliff -= (stackedValues[1] - 270 stackedValues[0]); 271 } 272 } 273 } 274 // When reversedStacks is true, loop up, 275 // else loop down 276 i += upOrDown; 277 } 278 } 279 pointMap[x][cliffName] = cliff; 280 }); 281 // There is no point for this X value in this series, so we 282 // insert a dummy point in order for the areas to be drawn 283 // correctly. 284 } 285 else { 286 // Loop down the stack to find the series below this 287 // one that has a value (#1991) 288 let i = seriesIndex; 289 while (i >= 0 && i < seriesLength) { 290 const si = yAxisSeries[i].index; 291 stackPoint = stack[x].points[si]; 292 if (stackPoint) { 293 y = stackPoint[1]; 294 break; 295 } 296 // When reversedStacks is true, loop up, else loop 297 // down 298 i += upOrDown; 299 } 300 y = pick(y, 0); 301 y = yAxis.translate(// #6272 302 y, 0, 1, 0, 1); 303 segment.push({ 304 isNull: true, 305 plotX: xAxis.translate(// #6272 306 x, 0, 0, 0, 1), 307 x: x, 308 plotY: y, 309 yBottom: y 310 }); 311 } 312 }); 313 } 314 return segment; 315 } 316} 317/* * 318 * 319 * Static Properties 320 * 321 * */ 322AreaSeries.defaultOptions = merge(LineSeries.defaultOptions, AreaSeriesDefaults); 323extend(AreaSeries.prototype, { 324 singleStacks: false 325});
326SeriesRegistry.registerSeriesType('area', AreaSeries); 327/* * 328 * 329 * Default Export 330 * 331 * */ 332export default AreaSeries;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.