corrade-nucleus-nucleons – Diff between revs 1 and 11
?pathlinks?
Rev 1 | Rev 11 | |||
---|---|---|---|---|
1 | /** |
1 | /** |
|
2 | * @license Highcharts JS v5.0.10 (2017-03-31) |
2 | * @license Highcharts JS v5.0.12 (2017-05-24) |
|
3 | * Data module |
3 | * Data module |
|
4 | * |
4 | * |
|
5 | * (c) 2012-2017 Torstein Honsi |
5 | * (c) 2012-2017 Torstein Honsi |
|
6 | * |
6 | * |
|
7 | * License: www.highcharts.com/license |
7 | * License: www.highcharts.com/license |
|
8 | */ |
8 | */ |
|
9 | 'use strict'; |
9 | 'use strict'; |
|
10 | (function(factory) { |
10 | (function(factory) { |
|
11 | if (typeof module === 'object' && module.exports) { |
11 | if (typeof module === 'object' && module.exports) { |
|
12 | module.exports = factory; |
12 | module.exports = factory; |
|
13 | } else { |
13 | } else { |
|
14 | factory(Highcharts); |
14 | factory(Highcharts); |
|
15 | } |
15 | } |
|
16 | }(function(Highcharts) { |
16 | }(function(Highcharts) { |
|
17 | (function(Highcharts) { |
17 | (function(Highcharts) { |
|
18 | /** |
18 | /** |
|
19 | * Data module |
19 | * Data module |
|
20 | * |
20 | * |
|
21 | * (c) 2012-2017 Torstein Honsi |
21 | * (c) 2012-2017 Torstein Honsi |
|
22 | * |
22 | * |
|
23 | * License: www.highcharts.com/license |
23 | * License: www.highcharts.com/license |
|
24 | */ |
24 | */ |
|
25 | |
25 | |
|
26 | /* global jQuery */ |
26 | /* global jQuery */ |
|
27 | |
27 | |
|
28 | // Utilities |
28 | // Utilities |
|
29 | var win = Highcharts.win, |
29 | var win = Highcharts.win, |
|
30 | doc = win.document, |
30 | doc = win.document, |
|
31 | each = Highcharts.each, |
31 | each = Highcharts.each, |
|
- | 32 | objectEach = Highcharts.objectEach, |
||
32 | pick = Highcharts.pick, |
33 | pick = Highcharts.pick, |
|
33 | inArray = Highcharts.inArray, |
34 | inArray = Highcharts.inArray, |
|
34 | isNumber = Highcharts.isNumber, |
35 | isNumber = Highcharts.isNumber, |
|
35 | splat = Highcharts.splat, |
36 | splat = Highcharts.splat, |
|
36 | SeriesBuilder; |
37 | SeriesBuilder; |
|
37 | |
38 | |
|
38 | |
39 | |
|
39 | // The Data constructor |
40 | // The Data constructor |
|
40 | var Data = function(dataOptions, chartOptions) { |
41 | var Data = function(dataOptions, chartOptions) { |
|
41 | this.init(dataOptions, chartOptions); |
42 | this.init(dataOptions, chartOptions); |
|
42 | }; |
43 | }; |
|
43 | |
44 | |
|
44 | // Set the prototype properties |
45 | // Set the prototype properties |
|
45 | Highcharts.extend(Data.prototype, { |
46 | Highcharts.extend(Data.prototype, { |
|
46 | |
47 | |
|
47 | /** |
48 | /** |
|
48 | * Initialize the Data object with the given options |
49 | * Initialize the Data object with the given options |
|
49 | */ |
50 | */ |
|
50 | init: function(options, chartOptions) { |
51 | init: function(options, chartOptions) { |
|
51 | this.options = options; |
52 | this.options = options; |
|
52 | this.chartOptions = chartOptions; |
53 | this.chartOptions = chartOptions; |
|
53 | this.columns = options.columns || this.rowsToColumns(options.rows) || []; |
54 | this.columns = options.columns || this.rowsToColumns(options.rows) || []; |
|
54 | this.firstRowAsNames = pick(options.firstRowAsNames, true); |
55 | this.firstRowAsNames = pick(options.firstRowAsNames, true); |
|
55 | this.decimalRegex = options.decimalPoint && new RegExp('^(-?[0-9]+)' + options.decimalPoint + '([0-9]+)$'); |
56 | this.decimalRegex = options.decimalPoint && new RegExp('^(-?[0-9]+)' + options.decimalPoint + '([0-9]+)$'); |
|
56 | |
57 | |
|
57 | // This is a two-dimensional array holding the raw, trimmed string values |
58 | // This is a two-dimensional array holding the raw, trimmed string values |
|
58 | // with the same organisation as the columns array. It makes it possible |
59 | // with the same organisation as the columns array. It makes it possible |
|
59 | // for example to revert from interpreted timestamps to string-based |
60 | // for example to revert from interpreted timestamps to string-based |
|
60 | // categories. |
61 | // categories. |
|
61 | this.rawColumns = []; |
62 | this.rawColumns = []; |
|
62 | |
63 | |
|
63 | // No need to parse or interpret anything |
64 | // No need to parse or interpret anything |
|
64 | if (this.columns.length) { |
65 | if (this.columns.length) { |
|
65 | this.dataFound(); |
66 | this.dataFound(); |
|
66 | |
67 | |
|
67 | // Parse and interpret |
68 | // Parse and interpret |
|
68 | } else { |
69 | } else { |
|
69 | |
70 | |
|
70 | // Parse a CSV string if options.csv is given |
71 | // Parse a CSV string if options.csv is given |
|
71 | this.parseCSV(); |
72 | this.parseCSV(); |
|
72 | |
73 | |
|
73 | // Parse a HTML table if options.table is given |
74 | // Parse a HTML table if options.table is given |
|
74 | this.parseTable(); |
75 | this.parseTable(); |
|
75 | |
76 | |
|
76 | // Parse a Google Spreadsheet |
77 | // Parse a Google Spreadsheet |
|
77 | this.parseGoogleSpreadsheet(); |
78 | this.parseGoogleSpreadsheet(); |
|
78 | } |
79 | } |
|
79 | |
80 | |
|
80 | }, |
81 | }, |
|
81 | |
82 | |
|
82 | /** |
83 | /** |
|
83 | * Get the column distribution. For example, a line series takes a single column for |
84 | * Get the column distribution. For example, a line series takes a single column for |
|
84 | * Y values. A range series takes two columns for low and high values respectively, |
85 | * Y values. A range series takes two columns for low and high values respectively, |
|
85 | * and an OHLC series takes four columns. |
86 | * and an OHLC series takes four columns. |
|
86 | */ |
87 | */ |
|
87 | getColumnDistribution: function() { |
88 | getColumnDistribution: function() { |
|
88 | var chartOptions = this.chartOptions, |
89 | var chartOptions = this.chartOptions, |
|
89 | options = this.options, |
90 | options = this.options, |
|
90 | xColumns = [], |
91 | xColumns = [], |
|
91 | getValueCount = function(type) { |
92 | getValueCount = function(type) { |
|
92 | return (Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap || [0]).length; |
93 | return (Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap || [0]).length; |
|
93 | }, |
94 | }, |
|
94 | getPointArrayMap = function(type) { |
95 | getPointArrayMap = function(type) { |
|
95 | return Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap; |
96 | return Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap; |
|
96 | }, |
97 | }, |
|
97 | globalType = chartOptions && chartOptions.chart && chartOptions.chart.type, |
98 | globalType = chartOptions && chartOptions.chart && chartOptions.chart.type, |
|
98 | individualCounts = [], |
99 | individualCounts = [], |
|
99 | seriesBuilders = [], |
100 | seriesBuilders = [], |
|
100 | seriesIndex = 0, |
101 | seriesIndex = 0, |
|
101 | i; |
102 | i; |
|
102 | |
103 | |
|
103 | each((chartOptions && chartOptions.series) || [], function(series) { |
104 | each((chartOptions && chartOptions.series) || [], function(series) { |
|
104 | individualCounts.push(getValueCount(series.type || globalType)); |
105 | individualCounts.push(getValueCount(series.type || globalType)); |
|
105 | }); |
106 | }); |
|
106 | |
107 | |
|
107 | // Collect the x-column indexes from seriesMapping |
108 | // Collect the x-column indexes from seriesMapping |
|
108 | each((options && options.seriesMapping) || [], function(mapping) { |
109 | each((options && options.seriesMapping) || [], function(mapping) { |
|
109 | xColumns.push(mapping.x || 0); |
110 | xColumns.push(mapping.x || 0); |
|
110 | }); |
111 | }); |
|
111 | |
112 | |
|
112 | // If there are no defined series with x-columns, use the first column as x column |
113 | // If there are no defined series with x-columns, use the first column as x column |
|
113 | if (xColumns.length === 0) { |
114 | if (xColumns.length === 0) { |
|
114 | xColumns.push(0); |
115 | xColumns.push(0); |
|
115 | } |
116 | } |
|
116 | |
117 | |
|
117 | // Loop all seriesMappings and constructs SeriesBuilders from |
118 | // Loop all seriesMappings and constructs SeriesBuilders from |
|
118 | // the mapping options. |
119 | // the mapping options. |
|
119 | each((options && options.seriesMapping) || [], function(mapping) { |
120 | each((options && options.seriesMapping) || [], function(mapping) { |
|
120 | var builder = new SeriesBuilder(), |
121 | var builder = new SeriesBuilder(), |
|
121 | name, |
- | ||
122 | numberOfValueColumnsNeeded = individualCounts[seriesIndex] || getValueCount(globalType), |
122 | numberOfValueColumnsNeeded = individualCounts[seriesIndex] || getValueCount(globalType), |
|
123 | seriesArr = (chartOptions && chartOptions.series) || [], |
123 | seriesArr = (chartOptions && chartOptions.series) || [], |
|
124 | series = seriesArr[seriesIndex] || {}, |
124 | series = seriesArr[seriesIndex] || {}, |
|
125 | pointArrayMap = getPointArrayMap(series.type || globalType) || ['y']; |
125 | pointArrayMap = getPointArrayMap(series.type || globalType) || ['y']; |
|
126 | |
126 | |
|
127 | // Add an x reader from the x property or from an undefined column |
127 | // Add an x reader from the x property or from an undefined column |
|
128 | // if the property is not set. It will then be auto populated later. |
128 | // if the property is not set. It will then be auto populated later. |
|
129 | builder.addColumnReader(mapping.x, 'x'); |
129 | builder.addColumnReader(mapping.x, 'x'); |
|
130 | |
130 | |
|
131 | // Add all column mappings |
131 | // Add all column mappings |
|
132 | for (name in mapping) { |
132 | objectEach(mapping, function(val, name) { |
|
133 | if (mapping.hasOwnProperty(name) && name !== 'x') { |
133 | if (name !== 'x') { |
|
134 | builder.addColumnReader(mapping[name], name); |
134 | builder.addColumnReader(val, name); |
|
135 | } |
135 | } |
|
136 | } |
136 | }); |
|
137 | |
137 | |
|
138 | // Add missing columns |
138 | // Add missing columns |
|
139 | for (i = 0; i < numberOfValueColumnsNeeded; i++) { |
139 | for (i = 0; i < numberOfValueColumnsNeeded; i++) { |
|
140 | if (!builder.hasReader(pointArrayMap[i])) { |
140 | if (!builder.hasReader(pointArrayMap[i])) { |
|
141 | //builder.addNextColumnReader(pointArrayMap[i]); |
141 | //builder.addNextColumnReader(pointArrayMap[i]); |
|
142 | // Create and add a column reader for the next free column index |
142 | // Create and add a column reader for the next free column index |
|
143 | builder.addColumnReader(undefined, pointArrayMap[i]); |
143 | builder.addColumnReader(undefined, pointArrayMap[i]); |
|
144 | } |
144 | } |
|
145 | } |
145 | } |
|
146 | |
146 | |
|
147 | seriesBuilders.push(builder); |
147 | seriesBuilders.push(builder); |
|
148 | seriesIndex++; |
148 | seriesIndex++; |
|
149 | }); |
149 | }); |
|
150 | |
150 | |
|
151 | var globalPointArrayMap = getPointArrayMap(globalType); |
151 | var globalPointArrayMap = getPointArrayMap(globalType); |
|
152 | if (globalPointArrayMap === undefined) { |
152 | if (globalPointArrayMap === undefined) { |
|
153 | globalPointArrayMap = ['y']; |
153 | globalPointArrayMap = ['y']; |
|
154 | } |
154 | } |
|
155 | |
155 | |
|
156 | this.valueCount = { |
156 | this.valueCount = { |
|
157 | global: getValueCount(globalType), |
157 | global: getValueCount(globalType), |
|
158 | xColumns: xColumns, |
158 | xColumns: xColumns, |
|
159 | individual: individualCounts, |
159 | individual: individualCounts, |
|
160 | seriesBuilders: seriesBuilders, |
160 | seriesBuilders: seriesBuilders, |
|
161 | globalPointArrayMap: globalPointArrayMap |
161 | globalPointArrayMap: globalPointArrayMap |
|
162 | }; |
162 | }; |
|
163 | }, |
163 | }, |
|
164 | |
164 | |
|
165 | /** |
165 | /** |
|
166 | * When the data is parsed into columns, either by CSV, table, GS or direct input, |
166 | * When the data is parsed into columns, either by CSV, table, GS or direct input, |
|
167 | * continue with other operations. |
167 | * continue with other operations. |
|
168 | */ |
168 | */ |
|
169 | dataFound: function() { |
169 | dataFound: function() { |
|
170 | |
170 | |
|
171 | if (this.options.switchRowsAndColumns) { |
171 | if (this.options.switchRowsAndColumns) { |
|
172 | this.columns = this.rowsToColumns(this.columns); |
172 | this.columns = this.rowsToColumns(this.columns); |
|
173 | } |
173 | } |
|
174 | |
174 | |
|
175 | // Interpret the info about series and columns |
175 | // Interpret the info about series and columns |
|
176 | this.getColumnDistribution(); |
176 | this.getColumnDistribution(); |
|
177 | |
177 | |
|
178 | // Interpret the values into right types |
178 | // Interpret the values into right types |
|
179 | this.parseTypes(); |
179 | this.parseTypes(); |
|
180 | |
180 | |
|
181 | // Handle columns if a handleColumns callback is given |
181 | // Handle columns if a handleColumns callback is given |
|
182 | if (this.parsed() !== false) { |
182 | if (this.parsed() !== false) { |
|
183 | |
183 | |
|
184 | // Complete if a complete callback is given |
184 | // Complete if a complete callback is given |
|
185 | this.complete(); |
185 | this.complete(); |
|
186 | } |
186 | } |
|
187 | |
187 | |
|
188 | }, |
188 | }, |
|
189 | |
189 | |
|
190 | /** |
190 | /** |
|
191 | * Parse a CSV input string |
191 | * Parse a CSV input string |
|
192 | */ |
192 | */ |
|
193 | parseCSV: function() { |
193 | parseCSV: function() { |
|
194 | var self = this, |
194 | var self = this, |
|
195 | options = this.options, |
195 | options = this.options, |
|
196 | csv = options.csv, |
196 | csv = options.csv, |
|
197 | columns = this.columns, |
197 | columns = this.columns, |
|
198 | startRow = options.startRow || 0, |
198 | startRow = options.startRow || 0, |
|
199 | endRow = options.endRow || Number.MAX_VALUE, |
199 | endRow = options.endRow || Number.MAX_VALUE, |
|
200 | startColumn = options.startColumn || 0, |
200 | startColumn = options.startColumn || 0, |
|
201 | endColumn = options.endColumn || Number.MAX_VALUE, |
201 | endColumn = options.endColumn || Number.MAX_VALUE, |
|
202 | itemDelimiter, |
202 | itemDelimiter, |
|
203 | lines, |
203 | lines, |
|
204 | activeRowNo = 0; |
204 | activeRowNo = 0; |
|
205 | |
205 | |
|
206 | if (csv) { |
206 | if (csv) { |
|
207 | |
207 | |
|
208 | lines = csv |
208 | lines = csv |
|
209 | .replace(/\r\n/g, '\n') // Unix |
209 | .replace(/\r\n/g, '\n') // Unix |
|
210 | .replace(/\r/g, '\n') // Mac |
210 | .replace(/\r/g, '\n') // Mac |
|
211 | .split(options.lineDelimiter || '\n'); |
211 | .split(options.lineDelimiter || '\n'); |
|
212 | |
212 | |
|
213 | itemDelimiter = options.itemDelimiter || (csv.indexOf('\t') !== -1 ? '\t' : ','); |
213 | itemDelimiter = options.itemDelimiter || (csv.indexOf('\t') !== -1 ? '\t' : ','); |
|
214 | |
214 | |
|
215 | each(lines, function(line, rowNo) { |
215 | each(lines, function(line, rowNo) { |
|
216 | var trimmed = self.trim(line), |
216 | var trimmed = self.trim(line), |
|
217 | isComment = trimmed.indexOf('#') === 0, |
217 | isComment = trimmed.indexOf('#') === 0, |
|
218 | isBlank = trimmed === '', |
218 | isBlank = trimmed === '', |
|
219 | items; |
219 | items; |
|
220 | |
220 | |
|
221 | if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) { |
221 | if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) { |
|
222 | items = line.split(itemDelimiter); |
222 | items = line.split(itemDelimiter); |
|
223 | each(items, function(item, colNo) { |
223 | each(items, function(item, colNo) { |
|
224 | if (colNo >= startColumn && colNo <= endColumn) { |
224 | if (colNo >= startColumn && colNo <= endColumn) { |
|
225 | if (!columns[colNo - startColumn]) { |
225 | if (!columns[colNo - startColumn]) { |
|
226 | columns[colNo - startColumn] = []; |
226 | columns[colNo - startColumn] = []; |
|
227 | } |
227 | } |
|
228 | |
228 | |
|
229 | columns[colNo - startColumn][activeRowNo] = item; |
229 | columns[colNo - startColumn][activeRowNo] = item; |
|
230 | } |
230 | } |
|
231 | }); |
231 | }); |
|
232 | activeRowNo += 1; |
232 | activeRowNo += 1; |
|
233 | } |
233 | } |
|
234 | }); |
234 | }); |
|
235 | |
235 | |
|
236 | this.dataFound(); |
236 | this.dataFound(); |
|
237 | } |
237 | } |
|
238 | }, |
238 | }, |
|
239 | |
239 | |
|
240 | /** |
240 | /** |
|
241 | * Parse a HTML table |
241 | * Parse a HTML table |
|
242 | */ |
242 | */ |
|
243 | parseTable: function() { |
243 | parseTable: function() { |
|
244 | var options = this.options, |
244 | var options = this.options, |
|
245 | table = options.table, |
245 | table = options.table, |
|
246 | columns = this.columns, |
246 | columns = this.columns, |
|
247 | startRow = options.startRow || 0, |
247 | startRow = options.startRow || 0, |
|
248 | endRow = options.endRow || Number.MAX_VALUE, |
248 | endRow = options.endRow || Number.MAX_VALUE, |
|
249 | startColumn = options.startColumn || 0, |
249 | startColumn = options.startColumn || 0, |
|
250 | endColumn = options.endColumn || Number.MAX_VALUE; |
250 | endColumn = options.endColumn || Number.MAX_VALUE; |
|
251 | |
251 | |
|
252 | if (table) { |
252 | if (table) { |
|
253 | |
253 | |
|
254 | if (typeof table === 'string') { |
254 | if (typeof table === 'string') { |
|
255 | table = doc.getElementById(table); |
255 | table = doc.getElementById(table); |
|
256 | } |
256 | } |
|
257 | |
257 | |
|
258 | each(table.getElementsByTagName('tr'), function(tr, rowNo) { |
258 | each(table.getElementsByTagName('tr'), function(tr, rowNo) { |
|
259 | if (rowNo >= startRow && rowNo <= endRow) { |
259 | if (rowNo >= startRow && rowNo <= endRow) { |
|
260 | each(tr.children, function(item, colNo) { |
260 | each(tr.children, function(item, colNo) { |
|
261 | if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) { |
261 | if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) { |
|
262 | if (!columns[colNo - startColumn]) { |
262 | if (!columns[colNo - startColumn]) { |
|
263 | columns[colNo - startColumn] = []; |
263 | columns[colNo - startColumn] = []; |
|
264 | } |
264 | } |
|
265 | |
265 | |
|
266 | columns[colNo - startColumn][rowNo - startRow] = item.innerHTML; |
266 | columns[colNo - startColumn][rowNo - startRow] = item.innerHTML; |
|
267 | } |
267 | } |
|
268 | }); |
268 | }); |
|
269 | } |
269 | } |
|
270 | }); |
270 | }); |
|
271 | |
271 | |
|
272 | this.dataFound(); // continue |
272 | this.dataFound(); // continue |
|
273 | } |
273 | } |
|
274 | }, |
274 | }, |
|
275 | |
275 | |
|
276 | /** |
276 | /** |
|
277 | */ |
277 | */ |
|
278 | parseGoogleSpreadsheet: function() { |
278 | parseGoogleSpreadsheet: function() { |
|
279 | var self = this, |
279 | var self = this, |
|
280 | options = this.options, |
280 | options = this.options, |
|
281 | googleSpreadsheetKey = options.googleSpreadsheetKey, |
281 | googleSpreadsheetKey = options.googleSpreadsheetKey, |
|
282 | columns = this.columns, |
282 | columns = this.columns, |
|
283 | startRow = options.startRow || 0, |
283 | startRow = options.startRow || 0, |
|
284 | endRow = options.endRow || Number.MAX_VALUE, |
284 | endRow = options.endRow || Number.MAX_VALUE, |
|
285 | startColumn = options.startColumn || 0, |
285 | startColumn = options.startColumn || 0, |
|
286 | endColumn = options.endColumn || Number.MAX_VALUE, |
286 | endColumn = options.endColumn || Number.MAX_VALUE, |
|
287 | gr, // google row |
287 | gr, // google row |
|
288 | gc; // google column |
288 | gc; // google column |
|
289 | |
289 | |
|
290 | if (googleSpreadsheetKey) { |
290 | if (googleSpreadsheetKey) { |
|
291 | jQuery.ajax({ |
291 | jQuery.ajax({ |
|
292 | dataType: 'json', |
292 | dataType: 'json', |
|
293 | url: 'https://spreadsheets.google.com/feeds/cells/' + |
293 | url: 'https://spreadsheets.google.com/feeds/cells/' + |
|
294 | googleSpreadsheetKey + '/' + (options.googleSpreadsheetWorksheet || 'od6') + |
294 | googleSpreadsheetKey + '/' + (options.googleSpreadsheetWorksheet || 'od6') + |
|
295 | '/public/values?alt=json-in-script&callback=?', |
295 | '/public/values?alt=json-in-script&callback=?', |
|
296 | error: options.error, |
296 | error: options.error, |
|
297 | success: function(json) { |
297 | success: function(json) { |
|
298 | // Prepare the data from the spreadsheat |
298 | // Prepare the data from the spreadsheat |
|
299 | var cells = json.feed.entry, |
299 | var cells = json.feed.entry, |
|
300 | cell, |
300 | cell, |
|
301 | cellCount = cells.length, |
301 | cellCount = cells.length, |
|
302 | colCount = 0, |
302 | colCount = 0, |
|
303 | rowCount = 0, |
303 | rowCount = 0, |
|
304 | i; |
304 | i; |
|
305 | |
305 | |
|
306 | // First, find the total number of columns and rows that |
306 | // First, find the total number of columns and rows that |
|
307 | // are actually filled with data |
307 | // are actually filled with data |
|
308 | for (i = 0; i < cellCount; i++) { |
308 | for (i = 0; i < cellCount; i++) { |
|
309 | cell = cells[i]; |
309 | cell = cells[i]; |
|
310 | colCount = Math.max(colCount, cell.gs$cell.col); |
310 | colCount = Math.max(colCount, cell.gs$cell.col); |
|
311 | rowCount = Math.max(rowCount, cell.gs$cell.row); |
311 | rowCount = Math.max(rowCount, cell.gs$cell.row); |
|
312 | } |
312 | } |
|
313 | |
313 | |
|
314 | // Set up arrays containing the column data |
314 | // Set up arrays containing the column data |
|
315 | for (i = 0; i < colCount; i++) { |
315 | for (i = 0; i < colCount; i++) { |
|
316 | if (i >= startColumn && i <= endColumn) { |
316 | if (i >= startColumn && i <= endColumn) { |
|
317 | // Create new columns with the length of either end-start or rowCount |
317 | // Create new columns with the length of either end-start or rowCount |
|
318 | columns[i - startColumn] = []; |
318 | columns[i - startColumn] = []; |
|
319 | |
319 | |
|
320 | // Setting the length to avoid jslint warning |
320 | // Setting the length to avoid jslint warning |
|
321 | columns[i - startColumn].length = Math.min(rowCount, endRow - startRow); |
321 | columns[i - startColumn].length = Math.min(rowCount, endRow - startRow); |
|
322 | } |
322 | } |
|
323 | } |
323 | } |
|
324 | |
324 | |
|
325 | // Loop over the cells and assign the value to the right |
325 | // Loop over the cells and assign the value to the right |
|
326 | // place in the column arrays |
326 | // place in the column arrays |
|
327 | for (i = 0; i < cellCount; i++) { |
327 | for (i = 0; i < cellCount; i++) { |
|
328 | cell = cells[i]; |
328 | cell = cells[i]; |
|
329 | gr = cell.gs$cell.row - 1; // rows start at 1 |
329 | gr = cell.gs$cell.row - 1; // rows start at 1 |
|
330 | gc = cell.gs$cell.col - 1; // columns start at 1 |
330 | gc = cell.gs$cell.col - 1; // columns start at 1 |
|
331 | |
331 | |
|
332 | // If both row and col falls inside start and end |
332 | // If both row and col falls inside start and end |
|
333 | // set the transposed cell value in the newly created columns |
333 | // set the transposed cell value in the newly created columns |
|
334 | if (gc >= startColumn && gc <= endColumn && |
334 | if (gc >= startColumn && gc <= endColumn && |
|
335 | gr >= startRow && gr <= endRow) { |
335 | gr >= startRow && gr <= endRow) { |
|
336 | columns[gc - startColumn][gr - startRow] = cell.content.$t; |
336 | columns[gc - startColumn][gr - startRow] = cell.content.$t; |
|
337 | } |
337 | } |
|
338 | } |
338 | } |
|
339 | |
339 | |
|
340 | // Insert null for empty spreadsheet cells (#5298) |
340 | // Insert null for empty spreadsheet cells (#5298) |
|
341 | each(columns, function(column) { |
341 | each(columns, function(column) { |
|
342 | for (i = 0; i < column.length; i++) { |
342 | for (i = 0; i < column.length; i++) { |
|
343 | if (column[i] === undefined) { |
343 | if (column[i] === undefined) { |
|
344 | column[i] = null; |
344 | column[i] = null; |
|
345 | } |
345 | } |
|
346 | } |
346 | } |
|
347 | }); |
347 | }); |
|
348 | |
348 | |
|
349 | self.dataFound(); |
349 | self.dataFound(); |
|
350 | } |
350 | } |
|
351 | }); |
351 | }); |
|
352 | } |
352 | } |
|
353 | }, |
353 | }, |
|
354 | |
354 | |
|
355 | /** |
355 | /** |
|
356 | * Trim a string from whitespace |
356 | * Trim a string from whitespace |
|
357 | */ |
357 | */ |
|
358 | trim: function(str, inside) { |
358 | trim: function(str, inside) { |
|
359 | if (typeof str === 'string') { |
359 | if (typeof str === 'string') { |
|
360 | str = str.replace(/^\s+|\s+$/g, ''); |
360 | str = str.replace(/^\s+|\s+$/g, ''); |
|
361 | |
361 | |
|
362 | // Clear white space insdie the string, like thousands separators |
362 | // Clear white space insdie the string, like thousands separators |
|
363 | if (inside && /^[0-9\s]+$/.test(str)) { |
363 | if (inside && /^[0-9\s]+$/.test(str)) { |
|
364 | str = str.replace(/\s/g, ''); |
364 | str = str.replace(/\s/g, ''); |
|
365 | } |
365 | } |
|
366 | |
366 | |
|
367 | if (this.decimalRegex) { |
367 | if (this.decimalRegex) { |
|
368 | str = str.replace(this.decimalRegex, '$1.$2'); |
368 | str = str.replace(this.decimalRegex, '$1.$2'); |
|
369 | } |
369 | } |
|
370 | } |
370 | } |
|
371 | return str; |
371 | return str; |
|
372 | }, |
372 | }, |
|
373 | |
373 | |
|
374 | /** |
374 | /** |
|
375 | * Parse numeric cells in to number types and date types in to true dates. |
375 | * Parse numeric cells in to number types and date types in to true dates. |
|
376 | */ |
376 | */ |
|
377 | parseTypes: function() { |
377 | parseTypes: function() { |
|
378 | var columns = this.columns, |
378 | var columns = this.columns, |
|
379 | col = columns.length; |
379 | col = columns.length; |
|
380 | |
380 | |
|
381 | while (col--) { |
381 | while (col--) { |
|
382 | this.parseColumn(columns[col], col); |
382 | this.parseColumn(columns[col], col); |
|
383 | } |
383 | } |
|
384 | |
384 | |
|
385 | }, |
385 | }, |
|
386 | |
386 | |
|
387 | /** |
387 | /** |
|
388 | * Parse a single column. Set properties like .isDatetime and .isNumeric. |
388 | * Parse a single column. Set properties like .isDatetime and .isNumeric. |
|
389 | */ |
389 | */ |
|
390 | parseColumn: function(column, col) { |
390 | parseColumn: function(column, col) { |
|
391 | var rawColumns = this.rawColumns, |
391 | var rawColumns = this.rawColumns, |
|
392 | columns = this.columns, |
392 | columns = this.columns, |
|
393 | row = column.length, |
393 | row = column.length, |
|
394 | val, |
394 | val, |
|
395 | floatVal, |
395 | floatVal, |
|
396 | trimVal, |
396 | trimVal, |
|
397 | trimInsideVal, |
397 | trimInsideVal, |
|
398 | firstRowAsNames = this.firstRowAsNames, |
398 | firstRowAsNames = this.firstRowAsNames, |
|
399 | isXColumn = inArray(col, this.valueCount.xColumns) !== -1, |
399 | isXColumn = inArray(col, this.valueCount.xColumns) !== -1, |
|
400 | dateVal, |
400 | dateVal, |
|
401 | backup = [], |
401 | backup = [], |
|
402 | diff, |
402 | diff, |
|
403 | chartOptions = this.chartOptions, |
403 | chartOptions = this.chartOptions, |
|
404 | descending, |
404 | descending, |
|
405 | columnTypes = this.options.columnTypes || [], |
405 | columnTypes = this.options.columnTypes || [], |
|
406 | columnType = columnTypes[col], |
406 | columnType = columnTypes[col], |
|
407 | forceCategory = isXColumn && ((chartOptions && chartOptions.xAxis && splat(chartOptions.xAxis)[0].type === 'category') || columnType === 'string'); |
407 | forceCategory = isXColumn && ((chartOptions && chartOptions.xAxis && splat(chartOptions.xAxis)[0].type === 'category') || columnType === 'string'); |
|
408 | |
408 | |
|
409 | if (!rawColumns[col]) { |
409 | if (!rawColumns[col]) { |
|
410 | rawColumns[col] = []; |
410 | rawColumns[col] = []; |
|
411 | } |
411 | } |
|
412 | while (row--) { |
412 | while (row--) { |
|
413 | val = backup[row] || column[row]; |
413 | val = backup[row] || column[row]; |
|
414 | |
414 | |
|
415 | trimVal = this.trim(val); |
415 | trimVal = this.trim(val); |
|
416 | trimInsideVal = this.trim(val, true); |
416 | trimInsideVal = this.trim(val, true); |
|
417 | floatVal = parseFloat(trimInsideVal); |
417 | floatVal = parseFloat(trimInsideVal); |
|
418 | |
418 | |
|
419 | // Set it the first time |
419 | // Set it the first time |
|
420 | if (rawColumns[col][row] === undefined) { |
420 | if (rawColumns[col][row] === undefined) { |
|
421 | rawColumns[col][row] = trimVal; |
421 | rawColumns[col][row] = trimVal; |
|
422 | } |
422 | } |
|
423 | |
423 | |
|
424 | // Disable number or date parsing by setting the X axis type to category |
424 | // Disable number or date parsing by setting the X axis type to category |
|
425 | if (forceCategory || (row === 0 && firstRowAsNames)) { |
425 | if (forceCategory || (row === 0 && firstRowAsNames)) { |
|
426 | column[row] = trimVal; |
426 | column[row] = trimVal; |
|
427 | |
427 | |
|
428 | } else if (+trimInsideVal === floatVal) { // is numeric |
428 | } else if (+trimInsideVal === floatVal) { // is numeric |
|
429 | |
429 | |
|
430 | column[row] = floatVal; |
430 | column[row] = floatVal; |
|
431 | |
431 | |
|
432 | // If the number is greater than milliseconds in a year, assume datetime |
432 | // If the number is greater than milliseconds in a year, assume datetime |
|
433 | if (floatVal > 365 * 24 * 3600 * 1000 && columnType !== 'float') { |
433 | if (floatVal > 365 * 24 * 3600 * 1000 && columnType !== 'float') { |
|
434 | column.isDatetime = true; |
434 | column.isDatetime = true; |
|
435 | } else { |
435 | } else { |
|
436 | column.isNumeric = true; |
436 | column.isNumeric = true; |
|
437 | } |
437 | } |
|
438 | |
438 | |
|
439 | if (column[row + 1] !== undefined) { |
439 | if (column[row + 1] !== undefined) { |
|
440 | descending = floatVal > column[row + 1]; |
440 | descending = floatVal > column[row + 1]; |
|
441 | } |
441 | } |
|
442 | |
442 | |
|
443 | // String, continue to determine if it is a date string or really a string |
443 | // String, continue to determine if it is a date string or really a string |
|
444 | } else { |
444 | } else { |
|
445 | dateVal = this.parseDate(val); |
445 | dateVal = this.parseDate(val); |
|
446 | // Only allow parsing of dates if this column is an x-column |
446 | // Only allow parsing of dates if this column is an x-column |
|
447 | if (isXColumn && isNumber(dateVal) && columnType !== 'float') { // is date |
447 | if (isXColumn && isNumber(dateVal) && columnType !== 'float') { // is date |
|
448 | backup[row] = val; |
448 | backup[row] = val; |
|
449 | column[row] = dateVal; |
449 | column[row] = dateVal; |
|
450 | column.isDatetime = true; |
450 | column.isDatetime = true; |
|
451 | |
451 | |
|
452 | // Check if the dates are uniformly descending or ascending. If they |
452 | // Check if the dates are uniformly descending or ascending. If they |
|
453 | // are not, chances are that they are a different time format, so check |
453 | // are not, chances are that they are a different time format, so check |
|
454 | // for alternative. |
454 | // for alternative. |
|
455 | if (column[row + 1] !== undefined) { |
455 | if (column[row + 1] !== undefined) { |
|
456 | diff = dateVal > column[row + 1]; |
456 | diff = dateVal > column[row + 1]; |
|
457 | if (diff !== descending && descending !== undefined) { |
457 | if (diff !== descending && descending !== undefined) { |
|
458 | if (this.alternativeFormat) { |
458 | if (this.alternativeFormat) { |
|
459 | this.dateFormat = this.alternativeFormat; |
459 | this.dateFormat = this.alternativeFormat; |
|
460 | row = column.length; |
460 | row = column.length; |
|
461 | this.alternativeFormat = this.dateFormats[this.dateFormat].alternative; |
461 | this.alternativeFormat = this.dateFormats[this.dateFormat].alternative; |
|
462 | } else { |
462 | } else { |
|
463 | column.unsorted = true; |
463 | column.unsorted = true; |
|
464 | } |
464 | } |
|
465 | } |
465 | } |
|
466 | descending = diff; |
466 | descending = diff; |
|
467 | } |
467 | } |
|
468 | |
468 | |
|
469 | } else { // string |
469 | } else { // string |
|
470 | column[row] = trimVal === '' ? null : trimVal; |
470 | column[row] = trimVal === '' ? null : trimVal; |
|
471 | if (row !== 0 && (column.isDatetime || column.isNumeric)) { |
471 | if (row !== 0 && (column.isDatetime || column.isNumeric)) { |
|
472 | column.mixed = true; |
472 | column.mixed = true; |
|
473 | } |
473 | } |
|
474 | } |
474 | } |
|
475 | } |
475 | } |
|
476 | } |
476 | } |
|
477 | |
477 | |
|
478 | // If strings are intermixed with numbers or dates in a parsed column, it is an indication |
478 | // If strings are intermixed with numbers or dates in a parsed column, it is an indication |
|
479 | // that parsing went wrong or the data was not intended to display as numbers or dates and |
479 | // that parsing went wrong or the data was not intended to display as numbers or dates and |
|
480 | // parsing is too aggressive. Fall back to categories. Demonstrated in the |
480 | // parsing is too aggressive. Fall back to categories. Demonstrated in the |
|
481 | // highcharts/demo/column-drilldown sample. |
481 | // highcharts/demo/column-drilldown sample. |
|
482 | if (isXColumn && column.mixed) { |
482 | if (isXColumn && column.mixed) { |
|
483 | columns[col] = rawColumns[col]; |
483 | columns[col] = rawColumns[col]; |
|
484 | } |
484 | } |
|
485 | |
485 | |
|
486 | // If the 0 column is date or number and descending, reverse all columns. |
486 | // If the 0 column is date or number and descending, reverse all columns. |
|
487 | if (isXColumn && descending && this.options.sort) { |
487 | if (isXColumn && descending && this.options.sort) { |
|
488 | for (col = 0; col < columns.length; col++) { |
488 | for (col = 0; col < columns.length; col++) { |
|
489 | columns[col].reverse(); |
489 | columns[col].reverse(); |
|
490 | if (firstRowAsNames) { |
490 | if (firstRowAsNames) { |
|
491 | columns[col].unshift(columns[col].pop()); |
491 | columns[col].unshift(columns[col].pop()); |
|
492 | } |
492 | } |
|
493 | } |
493 | } |
|
494 | } |
494 | } |
|
495 | }, |
495 | }, |
|
496 | |
496 | |
|
497 | /** |
497 | /** |
|
498 | * A collection of available date formats, extendable from the outside to support |
498 | * A collection of available date formats, extendable from the outside to support |
|
499 | * custom date formats. |
499 | * custom date formats. |
|
500 | */ |
500 | */ |
|
501 | dateFormats: { |
501 | dateFormats: { |
|
502 | 'YYYY-mm-dd': { |
502 | 'YYYY-mm-dd': { |
|
503 | regex: /^([0-9]{4})[\-\/\.]([0-9]{2})[\-\/\.]([0-9]{2})$/, |
503 | regex: /^([0-9]{4})[\-\/\.]([0-9]{2})[\-\/\.]([0-9]{2})$/, |
|
504 | parser: function(match) { |
504 | parser: function(match) { |
|
505 | return Date.UTC(+match[1], match[2] - 1, +match[3]); |
505 | return Date.UTC(+match[1], match[2] - 1, +match[3]); |
|
506 | } |
506 | } |
|
507 | }, |
507 | }, |
|
508 | 'dd/mm/YYYY': { |
508 | 'dd/mm/YYYY': { |
|
509 | regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/, |
509 | regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/, |
|
510 | parser: function(match) { |
510 | parser: function(match) { |
|
511 | return Date.UTC(+match[3], match[2] - 1, +match[1]); |
511 | return Date.UTC(+match[3], match[2] - 1, +match[1]); |
|
512 | }, |
512 | }, |
|
513 | alternative: 'mm/dd/YYYY' // different format with the same regex |
513 | alternative: 'mm/dd/YYYY' // different format with the same regex |
|
514 | }, |
514 | }, |
|
515 | 'mm/dd/YYYY': { |
515 | 'mm/dd/YYYY': { |
|
516 | regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/, |
516 | regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/, |
|
517 | parser: function(match) { |
517 | parser: function(match) { |
|
518 | return Date.UTC(+match[3], match[1] - 1, +match[2]); |
518 | return Date.UTC(+match[3], match[1] - 1, +match[2]); |
|
519 | } |
519 | } |
|
520 | }, |
520 | }, |
|
521 | 'dd/mm/YY': { |
521 | 'dd/mm/YY': { |
|
522 | regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/, |
522 | regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/, |
|
523 | parser: function(match) { |
523 | parser: function(match) { |
|
524 | return Date.UTC(+match[3] + 2000, match[2] - 1, +match[1]); |
524 | return Date.UTC(+match[3] + 2000, match[2] - 1, +match[1]); |
|
525 | }, |
525 | }, |
|
526 | alternative: 'mm/dd/YY' // different format with the same regex |
526 | alternative: 'mm/dd/YY' // different format with the same regex |
|
527 | }, |
527 | }, |
|
528 | 'mm/dd/YY': { |
528 | 'mm/dd/YY': { |
|
529 | regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/, |
529 | regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/, |
|
530 | parser: function(match) { |
530 | parser: function(match) { |
|
531 | return Date.UTC(+match[3] + 2000, match[1] - 1, +match[2]); |
531 | return Date.UTC(+match[3] + 2000, match[1] - 1, +match[2]); |
|
532 | } |
532 | } |
|
533 | } |
533 | } |
|
534 | }, |
534 | }, |
|
535 | |
535 | |
|
536 | /** |
536 | /** |
|
537 | * Parse a date and return it as a number. Overridable through options.parseDate. |
537 | * Parse a date and return it as a number. Overridable through options.parseDate. |
|
538 | */ |
538 | */ |
|
539 | parseDate: function(val) { |
539 | parseDate: function(val) { |
|
540 | var parseDate = this.options.parseDate, |
540 | var parseDate = this.options.parseDate, |
|
541 | ret, |
541 | ret, |
|
542 | key, |
542 | key, |
|
543 | format, |
543 | format, |
|
544 | dateFormat = this.options.dateFormat || this.dateFormat, |
544 | dateFormat = this.options.dateFormat || this.dateFormat, |
|
545 | match; |
545 | match; |
|
546 | |
546 | |
|
547 | if (parseDate) { |
547 | if (parseDate) { |
|
548 | ret = parseDate(val); |
548 | ret = parseDate(val); |
|
549 | |
549 | |
|
550 | } else if (typeof val === 'string') { |
550 | } else if (typeof val === 'string') { |
|
551 | // Auto-detect the date format the first time |
551 | // Auto-detect the date format the first time |
|
552 | if (!dateFormat) { |
552 | if (!dateFormat) { |
|
553 | for (key in this.dateFormats) { |
553 | for (key in this.dateFormats) { |
|
554 | format = this.dateFormats[key]; |
554 | format = this.dateFormats[key]; |
|
555 | match = val.match(format.regex); |
555 | match = val.match(format.regex); |
|
556 | if (match) { |
556 | if (match) { |
|
557 | this.dateFormat = dateFormat = key; |
557 | this.dateFormat = dateFormat = key; |
|
558 | this.alternativeFormat = format.alternative; |
558 | this.alternativeFormat = format.alternative; |
|
559 | ret = format.parser(match); |
559 | ret = format.parser(match); |
|
560 | break; |
560 | break; |
|
561 | } |
561 | } |
|
562 | } |
562 | } |
|
563 | // Next time, use the one previously found |
563 | // Next time, use the one previously found |
|
564 | } else { |
564 | } else { |
|
565 | format = this.dateFormats[dateFormat]; |
565 | format = this.dateFormats[dateFormat]; |
|
566 | match = val.match(format.regex); |
566 | match = val.match(format.regex); |
|
567 | if (match) { |
567 | if (match) { |
|
568 | ret = format.parser(match); |
568 | ret = format.parser(match); |
|
569 | } |
569 | } |
|
570 | } |
570 | } |
|
571 | // Fall back to Date.parse |
571 | // Fall back to Date.parse |
|
572 | if (!match) { |
572 | if (!match) { |
|
573 | match = Date.parse(val); |
573 | match = Date.parse(val); |
|
574 | // External tools like Date.js and MooTools extend Date object and |
574 | // External tools like Date.js and MooTools extend Date object and |
|
575 | // returns a date. |
575 | // returns a date. |
|
576 | if (typeof match === 'object' && match !== null && match.getTime) { |
576 | if (typeof match === 'object' && match !== null && match.getTime) { |
|
577 | ret = match.getTime() - match.getTimezoneOffset() * 60000; |
577 | ret = match.getTime() - match.getTimezoneOffset() * 60000; |
|
578 | |
578 | |
|
579 | // Timestamp |
579 | // Timestamp |
|
580 | } else if (isNumber(match)) { |
580 | } else if (isNumber(match)) { |
|
581 | ret = match - (new Date(match)).getTimezoneOffset() * 60000; |
581 | ret = match - (new Date(match)).getTimezoneOffset() * 60000; |
|
582 | } |
582 | } |
|
583 | } |
583 | } |
|
584 | } |
584 | } |
|
585 | return ret; |
585 | return ret; |
|
586 | }, |
586 | }, |
|
587 | |
587 | |
|
588 | /** |
588 | /** |
|
589 | * Reorganize rows into columns |
589 | * Reorganize rows into columns |
|
590 | */ |
590 | */ |
|
591 | rowsToColumns: function(rows) { |
591 | rowsToColumns: function(rows) { |
|
592 | var row, |
592 | var row, |
|
593 | rowsLength, |
593 | rowsLength, |
|
594 | col, |
594 | col, |
|
595 | colsLength, |
595 | colsLength, |
|
596 | columns; |
596 | columns; |
|
597 | |
597 | |
|
598 | if (rows) { |
598 | if (rows) { |
|
599 | columns = []; |
599 | columns = []; |
|
600 | rowsLength = rows.length; |
600 | rowsLength = rows.length; |
|
601 | for (row = 0; row < rowsLength; row++) { |
601 | for (row = 0; row < rowsLength; row++) { |
|
602 | colsLength = rows[row].length; |
602 | colsLength = rows[row].length; |
|
603 | for (col = 0; col < colsLength; col++) { |
603 | for (col = 0; col < colsLength; col++) { |
|
604 | if (!columns[col]) { |
604 | if (!columns[col]) { |
|
605 | columns[col] = []; |
605 | columns[col] = []; |
|
606 | } |
606 | } |
|
607 | columns[col][row] = rows[row][col]; |
607 | columns[col][row] = rows[row][col]; |
|
608 | } |
608 | } |
|
609 | } |
609 | } |
|
610 | } |
610 | } |
|
611 | return columns; |
611 | return columns; |
|
612 | }, |
612 | }, |
|
613 | |
613 | |
|
614 | /** |
614 | /** |
|
615 | * A hook for working directly on the parsed columns |
615 | * A hook for working directly on the parsed columns |
|
616 | */ |
616 | */ |
|
617 | parsed: function() { |
617 | parsed: function() { |
|
618 | if (this.options.parsed) { |
618 | if (this.options.parsed) { |
|
619 | return this.options.parsed.call(this, this.columns); |
619 | return this.options.parsed.call(this, this.columns); |
|
620 | } |
620 | } |
|
621 | }, |
621 | }, |
|
622 | |
622 | |
|
623 | getFreeIndexes: function(numberOfColumns, seriesBuilders) { |
623 | getFreeIndexes: function(numberOfColumns, seriesBuilders) { |
|
624 | var s, |
624 | var s, |
|
625 | i, |
625 | i, |
|
626 | freeIndexes = [], |
626 | freeIndexes = [], |
|
627 | freeIndexValues = [], |
627 | freeIndexValues = [], |
|
628 | referencedIndexes; |
628 | referencedIndexes; |
|
629 | |
629 | |
|
630 | // Add all columns as free |
630 | // Add all columns as free |
|
631 | for (i = 0; i < numberOfColumns; i = i + 1) { |
631 | for (i = 0; i < numberOfColumns; i = i + 1) { |
|
632 | freeIndexes.push(true); |
632 | freeIndexes.push(true); |
|
633 | } |
633 | } |
|
634 | |
634 | |
|
635 | // Loop all defined builders and remove their referenced columns |
635 | // Loop all defined builders and remove their referenced columns |
|
636 | for (s = 0; s < seriesBuilders.length; s = s + 1) { |
636 | for (s = 0; s < seriesBuilders.length; s = s + 1) { |
|
637 | referencedIndexes = seriesBuilders[s].getReferencedColumnIndexes(); |
637 | referencedIndexes = seriesBuilders[s].getReferencedColumnIndexes(); |
|
638 | |
638 | |
|
639 | for (i = 0; i < referencedIndexes.length; i = i + 1) { |
639 | for (i = 0; i < referencedIndexes.length; i = i + 1) { |
|
640 | freeIndexes[referencedIndexes[i]] = false; |
640 | freeIndexes[referencedIndexes[i]] = false; |
|
641 | } |
641 | } |
|
642 | } |
642 | } |
|
643 | |
643 | |
|
644 | // Collect the values for the free indexes |
644 | // Collect the values for the free indexes |
|
645 | for (i = 0; i < freeIndexes.length; i = i + 1) { |
645 | for (i = 0; i < freeIndexes.length; i = i + 1) { |
|
646 | if (freeIndexes[i]) { |
646 | if (freeIndexes[i]) { |
|
647 | freeIndexValues.push(i); |
647 | freeIndexValues.push(i); |
|
648 | } |
648 | } |
|
649 | } |
649 | } |
|
650 | |
650 | |
|
651 | return freeIndexValues; |
651 | return freeIndexValues; |
|
652 | }, |
652 | }, |
|
653 | |
653 | |
|
654 | /** |
654 | /** |
|
655 | * If a complete callback function is provided in the options, interpret the |
655 | * If a complete callback function is provided in the options, interpret the |
|
656 | * columns into a Highcharts options object. |
656 | * columns into a Highcharts options object. |
|
657 | */ |
657 | */ |
|
658 | complete: function() { |
658 | complete: function() { |
|
659 | |
659 | |
|
660 | var columns = this.columns, |
660 | var columns = this.columns, |
|
661 | xColumns = [], |
661 | xColumns = [], |
|
662 | type, |
662 | type, |
|
663 | options = this.options, |
663 | options = this.options, |
|
664 | series, |
664 | series, |
|
665 | data, |
665 | data, |
|
666 | i, |
666 | i, |
|
667 | j, |
667 | j, |
|
668 | r, |
668 | r, |
|
669 | seriesIndex, |
669 | seriesIndex, |
|
670 | chartOptions, |
670 | chartOptions, |
|
671 | allSeriesBuilders = [], |
671 | allSeriesBuilders = [], |
|
672 | builder, |
672 | builder, |
|
673 | freeIndexes, |
673 | freeIndexes, |
|
674 | typeCol, |
674 | typeCol, |
|
675 | index; |
675 | index; |
|
676 | |
676 | |
|
677 | xColumns.length = columns.length; |
677 | xColumns.length = columns.length; |
|
678 | if (options.complete || options.afterComplete) { |
678 | if (options.complete || options.afterComplete) { |
|
679 | |
679 | |
|
680 | // Get the names and shift the top row |
680 | // Get the names and shift the top row |
|
681 | for (i = 0; i < columns.length; i++) { |
681 | for (i = 0; i < columns.length; i++) { |
|
682 | if (this.firstRowAsNames) { |
682 | if (this.firstRowAsNames) { |
|
683 | columns[i].name = columns[i].shift(); |
683 | columns[i].name = columns[i].shift(); |
|
684 | } |
684 | } |
|
685 | } |
685 | } |
|
686 | |
686 | |
|
687 | // Use the next columns for series |
687 | // Use the next columns for series |
|
688 | series = []; |
688 | series = []; |
|
689 | freeIndexes = this.getFreeIndexes(columns.length, this.valueCount.seriesBuilders); |
689 | freeIndexes = this.getFreeIndexes(columns.length, this.valueCount.seriesBuilders); |
|
690 | |
690 | |
|
691 | // Populate defined series |
691 | // Populate defined series |
|
692 | for (seriesIndex = 0; seriesIndex < this.valueCount.seriesBuilders.length; seriesIndex++) { |
692 | for (seriesIndex = 0; seriesIndex < this.valueCount.seriesBuilders.length; seriesIndex++) { |
|
693 | builder = this.valueCount.seriesBuilders[seriesIndex]; |
693 | builder = this.valueCount.seriesBuilders[seriesIndex]; |
|
694 | |
694 | |
|
695 | // If the builder can be populated with remaining columns, then add it to allBuilders |
695 | // If the builder can be populated with remaining columns, then add it to allBuilders |
|
696 | if (builder.populateColumns(freeIndexes)) { |
696 | if (builder.populateColumns(freeIndexes)) { |
|
697 | allSeriesBuilders.push(builder); |
697 | allSeriesBuilders.push(builder); |
|
698 | } |
698 | } |
|
699 | } |
699 | } |
|
700 | |
700 | |
|
701 | // Populate dynamic series |
701 | // Populate dynamic series |
|
702 | while (freeIndexes.length > 0) { |
702 | while (freeIndexes.length > 0) { |
|
703 | builder = new SeriesBuilder(); |
703 | builder = new SeriesBuilder(); |
|
704 | builder.addColumnReader(0, 'x'); |
704 | builder.addColumnReader(0, 'x'); |
|
705 | |
705 | |
|
706 | // Mark index as used (not free) |
706 | // Mark index as used (not free) |
|
707 | index = inArray(0, freeIndexes); |
707 | index = inArray(0, freeIndexes); |
|
708 | if (index !== -1) { |
708 | if (index !== -1) { |
|
709 | freeIndexes.splice(index, 1); |
709 | freeIndexes.splice(index, 1); |
|
710 | } |
710 | } |
|
711 | |
711 | |
|
712 | for (i = 0; i < this.valueCount.global; i++) { |
712 | for (i = 0; i < this.valueCount.global; i++) { |
|
713 | // Create and add a column reader for the next free column index |
713 | // Create and add a column reader for the next free column index |
|
714 | builder.addColumnReader(undefined, this.valueCount.globalPointArrayMap[i]); |
714 | builder.addColumnReader(undefined, this.valueCount.globalPointArrayMap[i]); |
|
715 | } |
715 | } |
|
716 | |
716 | |
|
717 | // If the builder can be populated with remaining columns, then add it to allBuilders |
717 | // If the builder can be populated with remaining columns, then add it to allBuilders |
|
718 | if (builder.populateColumns(freeIndexes)) { |
718 | if (builder.populateColumns(freeIndexes)) { |
|
719 | allSeriesBuilders.push(builder); |
719 | allSeriesBuilders.push(builder); |
|
720 | } |
720 | } |
|
721 | } |
721 | } |
|
722 | |
722 | |
|
723 | // Get the data-type from the first series x column |
723 | // Get the data-type from the first series x column |
|
724 | if (allSeriesBuilders.length > 0 && allSeriesBuilders[0].readers.length > 0) { |
724 | if (allSeriesBuilders.length > 0 && allSeriesBuilders[0].readers.length > 0) { |
|
725 | typeCol = columns[allSeriesBuilders[0].readers[0].columnIndex]; |
725 | typeCol = columns[allSeriesBuilders[0].readers[0].columnIndex]; |
|
726 | if (typeCol !== undefined) { |
726 | if (typeCol !== undefined) { |
|
727 | if (typeCol.isDatetime) { |
727 | if (typeCol.isDatetime) { |
|
728 | type = 'datetime'; |
728 | type = 'datetime'; |
|
729 | } else if (!typeCol.isNumeric) { |
729 | } else if (!typeCol.isNumeric) { |
|
730 | type = 'category'; |
730 | type = 'category'; |
|
731 | } |
731 | } |
|
732 | } |
732 | } |
|
733 | } |
733 | } |
|
734 | // Axis type is category, then the "x" column should be called "name" |
734 | // Axis type is category, then the "x" column should be called "name" |
|
735 | if (type === 'category') { |
735 | if (type === 'category') { |
|
736 | for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) { |
736 | for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) { |
|
737 | builder = allSeriesBuilders[seriesIndex]; |
737 | builder = allSeriesBuilders[seriesIndex]; |
|
738 | for (r = 0; r < builder.readers.length; r++) { |
738 | for (r = 0; r < builder.readers.length; r++) { |
|
739 | if (builder.readers[r].configName === 'x') { |
739 | if (builder.readers[r].configName === 'x') { |
|
740 | builder.readers[r].configName = 'name'; |
740 | builder.readers[r].configName = 'name'; |
|
741 | } |
741 | } |
|
742 | } |
742 | } |
|
743 | } |
743 | } |
|
744 | } |
744 | } |
|
745 | |
745 | |
|
746 | // Read data for all builders |
746 | // Read data for all builders |
|
747 | for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) { |
747 | for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) { |
|
748 | builder = allSeriesBuilders[seriesIndex]; |
748 | builder = allSeriesBuilders[seriesIndex]; |
|
749 | |
749 | |
|
750 | // Iterate down the cells of each column and add data to the series |
750 | // Iterate down the cells of each column and add data to the series |
|
751 | data = []; |
751 | data = []; |
|
752 | for (j = 0; j < columns[0].length; j++) { |
752 | for (j = 0; j < columns[0].length; j++) { |
|
753 | data[j] = builder.read(columns, j); |
753 | data[j] = builder.read(columns, j); |
|
754 | } |
754 | } |
|
755 | |
755 | |
|
756 | // Add the series |
756 | // Add the series |
|
757 | series[seriesIndex] = { |
757 | series[seriesIndex] = { |
|
758 | data: data |
758 | data: data |
|
759 | }; |
759 | }; |
|
760 | if (builder.name) { |
760 | if (builder.name) { |
|
761 | series[seriesIndex].name = builder.name; |
761 | series[seriesIndex].name = builder.name; |
|
762 | } |
762 | } |
|
763 | if (type === 'category') { |
763 | if (type === 'category') { |
|
764 | series[seriesIndex].turboThreshold = 0; |
764 | series[seriesIndex].turboThreshold = 0; |
|
765 | } |
765 | } |
|
766 | } |
766 | } |
|
767 | |
767 | |
|
768 | |
768 | |
|
769 | |
769 | |
|
770 | // Do the callback |
770 | // Do the callback |
|
771 | chartOptions = { |
771 | chartOptions = { |
|
772 | series: series |
772 | series: series |
|
773 | }; |
773 | }; |
|
774 | if (type) { |
774 | if (type) { |
|
775 | chartOptions.xAxis = { |
775 | chartOptions.xAxis = { |
|
776 | type: type |
776 | type: type |
|
777 | }; |
777 | }; |
|
778 | if (type === 'category') { |
778 | if (type === 'category') { |
|
779 | chartOptions.xAxis.uniqueNames = false; |
779 | chartOptions.xAxis.uniqueNames = false; |
|
780 | } |
780 | } |
|
781 | } |
781 | } |
|
782 | |
782 | |
|
783 | if (options.complete) { |
783 | if (options.complete) { |
|
784 | options.complete(chartOptions); |
784 | options.complete(chartOptions); |
|
785 | } |
785 | } |
|
786 | |
786 | |
|
787 | // The afterComplete hook is used internally to avoid conflict with the externally |
787 | // The afterComplete hook is used internally to avoid conflict with the externally |
|
788 | // available complete option. |
788 | // available complete option. |
|
789 | if (options.afterComplete) { |
789 | if (options.afterComplete) { |
|
790 | options.afterComplete(chartOptions); |
790 | options.afterComplete(chartOptions); |
|
791 | } |
791 | } |
|
792 | } |
792 | } |
|
793 | } |
793 | } |
|
794 | }); |
794 | }); |
|
795 | |
795 | |
|
796 | // Register the Data prototype and data function on Highcharts |
796 | // Register the Data prototype and data function on Highcharts |
|
797 | Highcharts.Data = Data; |
797 | Highcharts.Data = Data; |
|
798 | Highcharts.data = function(options, chartOptions) { |
798 | Highcharts.data = function(options, chartOptions) { |
|
799 | return new Data(options, chartOptions); |
799 | return new Data(options, chartOptions); |
|
800 | }; |
800 | }; |
|
801 | |
801 | |
|
802 | // Extend Chart.init so that the Chart constructor accepts a new configuration |
802 | // Extend Chart.init so that the Chart constructor accepts a new configuration |
|
803 | // option group, data. |
803 | // option group, data. |
|
804 | Highcharts.wrap(Highcharts.Chart.prototype, 'init', function(proceed, userOptions, callback) { |
804 | Highcharts.wrap(Highcharts.Chart.prototype, 'init', function(proceed, userOptions, callback) { |
|
805 | var chart = this; |
805 | var chart = this; |
|
806 | |
806 | |
|
807 | if (userOptions && userOptions.data) { |
807 | if (userOptions && userOptions.data) { |
|
808 | Highcharts.data(Highcharts.extend(userOptions.data, { |
808 | Highcharts.data(Highcharts.extend(userOptions.data, { |
|
809 | |
809 | |
|
810 | afterComplete: function(dataOptions) { |
810 | afterComplete: function(dataOptions) { |
|
811 | var i, series; |
811 | var i, series; |
|
812 | |
812 | |
|
813 | // Merge series configs |
813 | // Merge series configs |
|
814 | if (userOptions.hasOwnProperty('series')) { |
814 | if (userOptions.hasOwnProperty('series')) { |
|
815 | if (typeof userOptions.series === 'object') { |
815 | if (typeof userOptions.series === 'object') { |
|
816 | i = Math.max(userOptions.series.length, dataOptions.series.length); |
816 | i = Math.max(userOptions.series.length, dataOptions.series.length); |
|
817 | while (i--) { |
817 | while (i--) { |
|
818 | series = userOptions.series[i] || {}; |
818 | series = userOptions.series[i] || {}; |
|
819 | userOptions.series[i] = Highcharts.merge(series, dataOptions.series[i]); |
819 | userOptions.series[i] = Highcharts.merge(series, dataOptions.series[i]); |
|
820 | } |
820 | } |
|
821 | } else { // Allow merging in dataOptions.series (#2856) |
821 | } else { // Allow merging in dataOptions.series (#2856) |
|
822 | delete userOptions.series; |
822 | delete userOptions.series; |
|
823 | } |
823 | } |
|
824 | } |
824 | } |
|
825 | |
825 | |
|
826 | // Do the merge |
826 | // Do the merge |
|
827 | userOptions = Highcharts.merge(dataOptions, userOptions); |
827 | userOptions = Highcharts.merge(dataOptions, userOptions); |
|
828 | |
828 | |
|
829 | proceed.call(chart, userOptions, callback); |
829 | proceed.call(chart, userOptions, callback); |
|
830 | } |
830 | } |
|
831 | }), userOptions); |
831 | }), userOptions); |
|
832 | } else { |
832 | } else { |
|
833 | proceed.call(chart, userOptions, callback); |
833 | proceed.call(chart, userOptions, callback); |
|
834 | } |
834 | } |
|
835 | }); |
835 | }); |
|
836 | |
836 | |
|
837 | /** |
837 | /** |
|
838 | * Creates a new SeriesBuilder. A SeriesBuilder consists of a number |
838 | * Creates a new SeriesBuilder. A SeriesBuilder consists of a number |
|
839 | * of ColumnReaders that reads columns and give them a name. |
839 | * of ColumnReaders that reads columns and give them a name. |
|
840 | * Ex: A series builder can be constructed to read column 3 as 'x' and |
840 | * Ex: A series builder can be constructed to read column 3 as 'x' and |
|
841 | * column 7 and 8 as 'y1' and 'y2'. |
841 | * column 7 and 8 as 'y1' and 'y2'. |
|
842 | * The output would then be points/rows of the form {x: 11, y1: 22, y2: 33} |
842 | * The output would then be points/rows of the form {x: 11, y1: 22, y2: 33} |
|
843 | * |
843 | * |
|
844 | * The name of the builder is taken from the second column. In the above |
844 | * The name of the builder is taken from the second column. In the above |
|
845 | * example it would be the column with index 7. |
845 | * example it would be the column with index 7. |
|
846 | * @constructor |
846 | * @constructor |
|
847 | */ |
847 | */ |
|
848 | SeriesBuilder = function() { |
848 | SeriesBuilder = function() { |
|
849 | this.readers = []; |
849 | this.readers = []; |
|
850 | this.pointIsArray = true; |
850 | this.pointIsArray = true; |
|
851 | }; |
851 | }; |
|
852 | |
852 | |
|
853 | /** |
853 | /** |
|
854 | * Populates readers with column indexes. A reader can be added without |
854 | * Populates readers with column indexes. A reader can be added without |
|
855 | * a specific index and for those readers the index is taken sequentially |
855 | * a specific index and for those readers the index is taken sequentially |
|
856 | * from the free columns (this is handled by the ColumnCursor instance). |
856 | * from the free columns (this is handled by the ColumnCursor instance). |
|
857 | * @returns {boolean} |
857 | * @returns {boolean} |
|
858 | */ |
858 | */ |
|
859 | SeriesBuilder.prototype.populateColumns = function(freeIndexes) { |
859 | SeriesBuilder.prototype.populateColumns = function(freeIndexes) { |
|
860 | var builder = this, |
860 | var builder = this, |
|
861 | enoughColumns = true; |
861 | enoughColumns = true; |
|
862 | |
862 | |
|
863 | // Loop each reader and give it an index if its missing. |
863 | // Loop each reader and give it an index if its missing. |
|
864 | // The freeIndexes.shift() will return undefined if there |
864 | // The freeIndexes.shift() will return undefined if there |
|
865 | // are no more columns. |
865 | // are no more columns. |
|
866 | each(builder.readers, function(reader) { |
866 | each(builder.readers, function(reader) { |
|
867 | if (reader.columnIndex === undefined) { |
867 | if (reader.columnIndex === undefined) { |
|
868 | reader.columnIndex = freeIndexes.shift(); |
868 | reader.columnIndex = freeIndexes.shift(); |
|
869 | } |
869 | } |
|
870 | }); |
870 | }); |
|
871 | |
871 | |
|
872 | // Now, all readers should have columns mapped. If not |
872 | // Now, all readers should have columns mapped. If not |
|
873 | // then return false to signal that this series should |
873 | // then return false to signal that this series should |
|
874 | // not be added. |
874 | // not be added. |
|
875 | each(builder.readers, function(reader) { |
875 | each(builder.readers, function(reader) { |
|
876 | if (reader.columnIndex === undefined) { |
876 | if (reader.columnIndex === undefined) { |
|
877 | enoughColumns = false; |
877 | enoughColumns = false; |
|
878 | } |
878 | } |
|
879 | }); |
879 | }); |
|
880 | |
880 | |
|
881 | return enoughColumns; |
881 | return enoughColumns; |
|
882 | }; |
882 | }; |
|
883 | |
883 | |
|
884 | /** |
884 | /** |
|
885 | * Reads a row from the dataset and returns a point or array depending |
885 | * Reads a row from the dataset and returns a point or array depending |
|
886 | * on the names of the readers. |
886 | * on the names of the readers. |
|
887 | * @param columns |
887 | * @param columns |
|
888 | * @param rowIndex |
888 | * @param rowIndex |
|
889 | * @returns {Array | Object} |
889 | * @returns {Array | Object} |
|
890 | */ |
890 | */ |
|
891 | SeriesBuilder.prototype.read = function(columns, rowIndex) { |
891 | SeriesBuilder.prototype.read = function(columns, rowIndex) { |
|
892 | var builder = this, |
892 | var builder = this, |
|
893 | pointIsArray = builder.pointIsArray, |
893 | pointIsArray = builder.pointIsArray, |
|
894 | point = pointIsArray ? [] : {}, |
894 | point = pointIsArray ? [] : {}, |
|
895 | columnIndexes; |
895 | columnIndexes; |
|
896 | |
896 | |
|
897 | // Loop each reader and ask it to read its value. |
897 | // Loop each reader and ask it to read its value. |
|
898 | // Then, build an array or point based on the readers names. |
898 | // Then, build an array or point based on the readers names. |
|
899 | each(builder.readers, function(reader) { |
899 | each(builder.readers, function(reader) { |
|
900 | var value = columns[reader.columnIndex][rowIndex]; |
900 | var value = columns[reader.columnIndex][rowIndex]; |
|
901 | if (pointIsArray) { |
901 | if (pointIsArray) { |
|
902 | point.push(value); |
902 | point.push(value); |
|
903 | } else { |
903 | } else { |
|
904 | point[reader.configName] = value; |
904 | point[reader.configName] = value; |
|
905 | } |
905 | } |
|
906 | }); |
906 | }); |
|
907 | |
907 | |
|
908 | // The name comes from the first column (excluding the x column) |
908 | // The name comes from the first column (excluding the x column) |
|
909 | if (this.name === undefined && builder.readers.length >= 2) { |
909 | if (this.name === undefined && builder.readers.length >= 2) { |
|
910 | columnIndexes = builder.getReferencedColumnIndexes(); |
910 | columnIndexes = builder.getReferencedColumnIndexes(); |
|
911 | if (columnIndexes.length >= 2) { |
911 | if (columnIndexes.length >= 2) { |
|
912 | // remove the first one (x col) |
912 | // remove the first one (x col) |
|
913 | columnIndexes.shift(); |
913 | columnIndexes.shift(); |
|
914 | |
914 | |
|
915 | // Sort the remaining |
915 | // Sort the remaining |
|
916 | columnIndexes.sort(); |
916 | columnIndexes.sort(); |
|
917 | |
917 | |
|
918 | // Now use the lowest index as name column |
918 | // Now use the lowest index as name column |
|
919 | this.name = columns[columnIndexes.shift()].name; |
919 | this.name = columns[columnIndexes.shift()].name; |
|
920 | } |
920 | } |
|
921 | } |
921 | } |
|
922 | |
922 | |
|
923 | return point; |
923 | return point; |
|
924 | }; |
924 | }; |
|
925 | |
925 | |
|
926 | /** |
926 | /** |
|
927 | * Creates and adds ColumnReader from the given columnIndex and configName. |
927 | * Creates and adds ColumnReader from the given columnIndex and configName. |
|
928 | * ColumnIndex can be undefined and in that case the reader will be given |
928 | * ColumnIndex can be undefined and in that case the reader will be given |
|
929 | * an index when columns are populated. |
929 | * an index when columns are populated. |
|
930 | * @param columnIndex {Number | undefined} |
930 | * @param columnIndex {Number | undefined} |
|
931 | * @param configName |
931 | * @param configName |
|
932 | */ |
932 | */ |
|
933 | SeriesBuilder.prototype.addColumnReader = function(columnIndex, configName) { |
933 | SeriesBuilder.prototype.addColumnReader = function(columnIndex, configName) { |
|
934 | this.readers.push({ |
934 | this.readers.push({ |
|
935 | columnIndex: columnIndex, |
935 | columnIndex: columnIndex, |
|
936 | configName: configName |
936 | configName: configName |
|
937 | }); |
937 | }); |
|
938 | |
938 | |
|
939 | if (!(configName === 'x' || configName === 'y' || configName === undefined)) { |
939 | if (!(configName === 'x' || configName === 'y' || configName === undefined)) { |
|
940 | this.pointIsArray = false; |
940 | this.pointIsArray = false; |
|
941 | } |
941 | } |
|
942 | }; |
942 | }; |
|
943 | |
943 | |
|
944 | /** |
944 | /** |
|
945 | * Returns an array of column indexes that the builder will use when |
945 | * Returns an array of column indexes that the builder will use when |
|
946 | * reading data. |
946 | * reading data. |
|
947 | * @returns {Array} |
947 | * @returns {Array} |
|
948 | */ |
948 | */ |
|
949 | SeriesBuilder.prototype.getReferencedColumnIndexes = function() { |
949 | SeriesBuilder.prototype.getReferencedColumnIndexes = function() { |
|
950 | var i, |
950 | var i, |
|
951 | referencedColumnIndexes = [], |
951 | referencedColumnIndexes = [], |
|
952 | columnReader; |
952 | columnReader; |
|
953 | |
953 | |
|
954 | for (i = 0; i < this.readers.length; i = i + 1) { |
954 | for (i = 0; i < this.readers.length; i = i + 1) { |
|
955 | columnReader = this.readers[i]; |
955 | columnReader = this.readers[i]; |
|
956 | if (columnReader.columnIndex !== undefined) { |
956 | if (columnReader.columnIndex !== undefined) { |
|
957 | referencedColumnIndexes.push(columnReader.columnIndex); |
957 | referencedColumnIndexes.push(columnReader.columnIndex); |
|
958 | } |
958 | } |
|
959 | } |
959 | } |
|
960 | |
960 | |
|
961 | return referencedColumnIndexes; |
961 | return referencedColumnIndexes; |
|
962 | }; |
962 | }; |
|
963 | |
963 | |
|
964 | /** |
964 | /** |
|
965 | * Returns true if the builder has a reader for the given configName. |
965 | * Returns true if the builder has a reader for the given configName. |
|
966 | * @param configName |
966 | * @param configName |
|
967 | * @returns {boolean} |
967 | * @returns {boolean} |
|
968 | */ |
968 | */ |
|
969 | SeriesBuilder.prototype.hasReader = function(configName) { |
969 | SeriesBuilder.prototype.hasReader = function(configName) { |
|
970 | var i, columnReader; |
970 | var i, columnReader; |
|
971 | for (i = 0; i < this.readers.length; i = i + 1) { |
971 | for (i = 0; i < this.readers.length; i = i + 1) { |
|
972 | columnReader = this.readers[i]; |
972 | columnReader = this.readers[i]; |
|
973 | if (columnReader.configName === configName) { |
973 | if (columnReader.configName === configName) { |
|
974 | return true; |
974 | return true; |
|
975 | } |
975 | } |
|
976 | } |
976 | } |
|
977 | // Else return undefined |
977 | // Else return undefined |
|
978 | }; |
978 | }; |
|
979 | |
979 | |
|
980 | }(Highcharts)); |
980 | }(Highcharts)); |
|
981 | })); |
981 | })); |
|
982 | |
982 | |