diff options
Diffstat (limited to 'html/includes/js/modules/data.src.js')
-rw-r--r-- | html/includes/js/modules/data.src.js | 1025 |
1 files changed, 0 insertions, 1025 deletions
diff --git a/html/includes/js/modules/data.src.js b/html/includes/js/modules/data.src.js deleted file mode 100644 index 471307f..0000000 --- a/html/includes/js/modules/data.src.js +++ /dev/null @@ -1,1025 +0,0 @@ -/** - * @license Data plugin for Highcharts - * - * (c) 2012-2014 Torstein Honsi - * - * License: www.highcharts.com/license - */ - -/* - * The Highcharts Data plugin is a utility to ease parsing of input sources like - * CSV, HTML tables or grid views into basic configuration options for use - * directly in the Highcharts constructor. - * - * Demo: http://jsfiddle.net/highcharts/SnLFj/ - * - * --- OPTIONS --- - * - * - columns : Array<Array<Mixed>> - * A two-dimensional array representing the input data on tabular form. This input can - * be used when the data is already parsed, for example from a grid view component. - * Each cell can be a string or number. If not switchRowsAndColumns is set, the columns - * are interpreted as series. See also the rows option. - * - * - complete : Function(chartOptions) - * The callback that is evaluated when the data is finished loading, optionally from an - * external source, and parsed. The first argument passed is a finished chart options - * object, containing the series. Thise options - * can be extended with additional options and passed directly to the chart constructor. This is - * related to the parsed callback, that goes in at an earlier stage. - * - * - csv : String - * A comma delimited string to be parsed. Related options are startRow, endRow, startColumn - * and endColumn to delimit what part of the table is used. The lineDelimiter and - * itemDelimiter options define the CSV delimiter formats. - * - * - dateFormat: String - * Which of the predefined date formats in Date.prototype.dateFormats to use to parse date - * columns, for example "dd/mm/YYYY" or "YYYY-mm-dd". Defaults to a best guess based on - * what format gives valid dates, and prefers ordered dates. - * - * - endColumn : Integer - * In tabular input data, the first row (indexed by 0) to use. Defaults to the last - * column containing data. - * - * - endRow : Integer - * In tabular input data, the last row (indexed by 0) to use. Defaults to the last row - * containing data. - * - * - googleSpreadsheetKey : String - * A Google Spreadsheet key. See https://developers.google.com/gdata/samples/spreadsheet_sample - * for general information on GS. - * - * - googleSpreadsheetWorksheet : String - * The Google Spreadsheet worksheet. The available id's can be read from - * https://spreadsheets.google.com/feeds/worksheets/{key}/public/basic - * - * - itemDelimiter : String - * Item or cell delimiter for parsing CSV. Defaults to the tab character "\t" if a tab character - * is found in the CSV string, if not it defaults to ",". - * - * - lineDelimiter : String - * Line delimiter for parsing CSV. Defaults to "\n". - * - * - parsed : Function - * A callback function to access the parsed columns, the two-dimentional input data - * array directly, before they are interpreted into series data and categories. See also - * the complete callback, that goes in on a later stage where the raw columns are interpreted - * into a Highcharts option structure. Return false to stop completion, or call this.complete() - * to continue async. - * - * - parseDate : Function - * A callback function to parse string representations of dates into JavaScript timestamps. - * Return an integer on success. - * - * - rows : Array<Array<Mixed>> - * The same as the columns input option, but defining rows intead of columns. - * - * - seriesMapping : Array<Object> - * An array containing object with Point property names along with what column id the - * property should be taken from. - * - * - startColumn : Integer - * In tabular input data, the first column (indexed by 0) to use. - * - * - startRow : Integer - * In tabular input data, the first row (indexed by 0) to use. - * - * - switchRowsAndColumns : Boolean - * Switch rows and columns of the input data, so that this.columns effectively becomes the - * rows of the data set, and the rows are interpreted as series. - * - * - table : String|HTMLElement - * A HTML table or the id of such to be parsed as input data. Related options ara startRow, - * endRow, startColumn and endColumn to delimit what part of the table is used. - */ - -/* - * TODO: - * - Handle various date formats - * - http://jsfiddle.net/highcharts/114wejdx/ - * - http://jsfiddle.net/highcharts/ryv67bkq/ - */ - -// JSLint options: -/*global jQuery, HighchartsAdapter */ - -(function (Highcharts) { // docs - - // Utilities - var each = Highcharts.each, - inArray = HighchartsAdapter.inArray, - splat = Highcharts.splat, - SeriesBuilder; - - - // The Data constructor - var Data = function (dataOptions, chartOptions) { - this.init(dataOptions, chartOptions); - }; - - // Set the prototype properties - Highcharts.extend(Data.prototype, { - - /** - * Initialize the Data object with the given options - */ - init: function (options, chartOptions) { - this.options = options; - this.chartOptions = chartOptions; - this.columns = options.columns || this.rowsToColumns(options.rows) || []; - - // This is a two-dimensional array holding the raw, trimmed string values - // with the same organisation as the columns array. It makes it possible - // for example to revert from interpreted timestamps to string-based - // categories. - this.rawColumns = []; - - // No need to parse or interpret anything - if (this.columns.length) { - this.dataFound(); - - // Parse and interpret - } else { - - // Parse a CSV string if options.csv is given - this.parseCSV(); - - // Parse a HTML table if options.table is given - this.parseTable(); - - // Parse a Google Spreadsheet - this.parseGoogleSpreadsheet(); - } - - }, - - /** - * Get the column distribution. For example, a line series takes a single column for - * Y values. A range series takes two columns for low and high values respectively, - * and an OHLC series takes four columns. - */ - getColumnDistribution: function () { - var chartOptions = this.chartOptions, - options = this.options, - xColumns = [], - getValueCount = function (type) { - return (Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap || [0]).length; - }, - getPointArrayMap = function (type) { - return Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap; - }, - globalType = chartOptions && chartOptions.chart && chartOptions.chart.type, - individualCounts = [], - seriesBuilders = [], - seriesIndex, - i; - - each((chartOptions && chartOptions.series) || [], function (series) { - individualCounts.push(getValueCount(series.type || globalType)); - }); - - // Collect the x-column indexes from seriesMapping - each((options && options.seriesMapping) || [], function (mapping) { - xColumns.push(mapping.x || 0); - }); - - // If there are no defined series with x-columns, use the first column as x column - if (xColumns.length === 0) { - xColumns.push(0); - } - - // Loop all seriesMappings and constructs SeriesBuilders from - // the mapping options. - each((options && options.seriesMapping) || [], function (mapping) { - var builder = new SeriesBuilder(), - name, - numberOfValueColumnsNeeded = individualCounts[seriesIndex] || getValueCount(globalType), - seriesArr = (chartOptions && chartOptions.series) || [], - series = seriesArr[seriesIndex] || {}, - pointArrayMap = getPointArrayMap(series.type || globalType) || ['y']; - - // Add an x reader from the x property or from an undefined column - // if the property is not set. It will then be auto populated later. - builder.addColumnReader(mapping.x, 'x'); - - // Add all column mappings - for (name in mapping) { - if (mapping.hasOwnProperty(name) && name !== 'x') { - builder.addColumnReader(mapping[name], name); - } - } - - // Add missing columns - for (i = 0; i < numberOfValueColumnsNeeded; i++) { - if (!builder.hasReader(pointArrayMap[i])) { - //builder.addNextColumnReader(pointArrayMap[i]); - // Create and add a column reader for the next free column index - builder.addColumnReader(undefined, pointArrayMap[i]); - } - } - - seriesBuilders.push(builder); - seriesIndex++; - }); - - var globalPointArrayMap = getPointArrayMap(globalType); - if (globalPointArrayMap === undefined) { - globalPointArrayMap = ['y']; - } - - this.valueCount = { - global: getValueCount(globalType), - xColumns: xColumns, - individual: individualCounts, - seriesBuilders: seriesBuilders, - globalPointArrayMap: globalPointArrayMap - }; - }, - - /** - * When the data is parsed into columns, either by CSV, table, GS or direct input, - * continue with other operations. - */ - dataFound: function () { - - if (this.options.switchRowsAndColumns) { - this.columns = this.rowsToColumns(this.columns); - } - - // Interpret the info about series and columns - this.getColumnDistribution(); - - // Interpret the values into right types - this.parseTypes(); - - // Use first row for series names? - this.findHeaderRow(); - - // Handle columns if a handleColumns callback is given - if (this.parsed() !== false) { - - // Complete if a complete callback is given - this.complete(); - } - - }, - - /** - * Parse a CSV input string - */ - parseCSV: function () { - var self = this, - options = this.options, - csv = options.csv, - columns = this.columns, - startRow = options.startRow || 0, - endRow = options.endRow || Number.MAX_VALUE, - startColumn = options.startColumn || 0, - endColumn = options.endColumn || Number.MAX_VALUE, - itemDelimiter, - lines, - activeRowNo = 0; - - if (csv) { - - lines = csv - .replace(/\r\n/g, "\n") // Unix - .replace(/\r/g, "\n") // Mac - .split(options.lineDelimiter || "\n"); - - itemDelimiter = options.itemDelimiter || (csv.indexOf('\t') !== -1 ? '\t' : ','); - - each(lines, function (line, rowNo) { - var trimmed = self.trim(line), - isComment = trimmed.indexOf('#') === 0, - isBlank = trimmed === '', - items; - - if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) { - items = line.split(itemDelimiter); - each(items, function (item, colNo) { - if (colNo >= startColumn && colNo <= endColumn) { - if (!columns[colNo - startColumn]) { - columns[colNo - startColumn] = []; - } - - columns[colNo - startColumn][activeRowNo] = item; - } - }); - activeRowNo += 1; - } - }); - - this.dataFound(); - } - }, - - /** - * Parse a HTML table - */ - parseTable: function () { - var options = this.options, - table = options.table, - columns = this.columns, - startRow = options.startRow || 0, - endRow = options.endRow || Number.MAX_VALUE, - startColumn = options.startColumn || 0, - endColumn = options.endColumn || Number.MAX_VALUE; - - if (table) { - - if (typeof table === 'string') { - table = document.getElementById(table); - } - - each(table.getElementsByTagName('tr'), function (tr, rowNo) { - if (rowNo >= startRow && rowNo <= endRow) { - each(tr.children, function (item, colNo) { - if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) { - if (!columns[colNo - startColumn]) { - columns[colNo - startColumn] = []; - } - - columns[colNo - startColumn][rowNo - startRow] = item.innerHTML; - } - }); - } - }); - - this.dataFound(); // continue - } - }, - - /** - */ - parseGoogleSpreadsheet: function () { - var self = this, - options = this.options, - googleSpreadsheetKey = options.googleSpreadsheetKey, - columns = this.columns, - startRow = options.startRow || 0, - endRow = options.endRow || Number.MAX_VALUE, - startColumn = options.startColumn || 0, - endColumn = options.endColumn || Number.MAX_VALUE, - gr, // google row - gc; // google column - - if (googleSpreadsheetKey) { - jQuery.ajax({ - dataType: 'json', - url: 'https://spreadsheets.google.com/feeds/cells/' + - googleSpreadsheetKey + '/' + (options.googleSpreadsheetWorksheet || 'od6') + - '/public/values?alt=json-in-script&callback=?', - error: options.error, - success: function (json) { - // Prepare the data from the spreadsheat - var cells = json.feed.entry, - cell, - cellCount = cells.length, - colCount = 0, - rowCount = 0, - i; - - // First, find the total number of columns and rows that - // are actually filled with data - for (i = 0; i < cellCount; i++) { - cell = cells[i]; - colCount = Math.max(colCount, cell.gs$cell.col); - rowCount = Math.max(rowCount, cell.gs$cell.row); - } - - // Set up arrays containing the column data - for (i = 0; i < colCount; i++) { - if (i >= startColumn && i <= endColumn) { - // Create new columns with the length of either end-start or rowCount - columns[i - startColumn] = []; - - // Setting the length to avoid jslint warning - columns[i - startColumn].length = Math.min(rowCount, endRow - startRow); - } - } - - // Loop over the cells and assign the value to the right - // place in the column arrays - for (i = 0; i < cellCount; i++) { - cell = cells[i]; - gr = cell.gs$cell.row - 1; // rows start at 1 - gc = cell.gs$cell.col - 1; // columns start at 1 - - // If both row and col falls inside start and end - // set the transposed cell value in the newly created columns - if (gc >= startColumn && gc <= endColumn && - gr >= startRow && gr <= endRow) { - columns[gc - startColumn][gr - startRow] = cell.content.$t; - } - } - self.dataFound(); - } - }); - } - }, - - /** - * Find the header row. For now, we just check whether the first row contains - * numbers or strings. Later we could loop down and find the first row with - * numbers. - */ - findHeaderRow: function () { - var headerRow = 0; - each(this.columns, function (column) { - if (column.isNumeric && typeof column[0] !== 'string') { - headerRow = null; - } - }); - this.headerRow = headerRow; - }, - - /** - * Trim a string from whitespace - */ - trim: function (str) { - return typeof str === 'string' ? str.replace(/^\s+|\s+$/g, '') : str; - }, - - /** - * Parse numeric cells in to number types and date types in to true dates. - */ - parseTypes: function () { - var columns = this.columns, - rawColumns = this.rawColumns, - col = columns.length, - row, - val, - floatVal, - trimVal, - isXColumn, - dateVal, - descending, - backup = [], - diff, - hasHeaderRow, - forceCategory, - chartOptions = this.chartOptions; - - while (col--) { - row = columns[col].length; - rawColumns[col] = []; - isXColumn = inArray(col, this.valueCount.xColumns) !== -1; - forceCategory = isXColumn && chartOptions && chartOptions.xAxis && splat(chartOptions.xAxis)[0].type === 'category'; - while (row--) { - val = backup[row] || columns[col][row]; - floatVal = parseFloat(val); - trimVal = rawColumns[col][row] = this.trim(val); - - // Disable number or date parsing by setting the X axis type to category - if (forceCategory) { - columns[col][row] = trimVal; - - /*jslint eqeq: true*/ - } else if (trimVal == floatVal) { // is numeric - /*jslint eqeq: false*/ - columns[col][row] = floatVal; - - // If the number is greater than milliseconds in a year, assume datetime - if (floatVal > 365 * 24 * 3600 * 1000) { - columns[col].isDatetime = true; - } else { - columns[col].isNumeric = true; - } - - } else { // string, continue to determine if it is a date string or really a string - dateVal = this.parseDate(val); - // Only allow parsing of dates if this column is an x-column - if (isXColumn && typeof dateVal === 'number' && !isNaN(dateVal)) { // is date - backup[row] = val; - columns[col][row] = dateVal; - columns[col].isDatetime = true; - - // Check if the dates are uniformly descending or ascending. If they - // are not, chances are that they are a different time format, so check - // for alternative. - if (columns[col][row + 1] !== undefined) { - diff = dateVal > columns[col][row + 1]; - if (diff !== descending && descending !== undefined) { - if (this.alternativeFormat) { - this.dateFormat = this.alternativeFormat; - row = columns[col].length; - this.alternativeFormat = this.dateFormats[this.dateFormat].alternative; - } else { - columns[col].unsorted = true; - } - } - descending = diff; - } - - } else { // string - columns[col][row] = trimVal === '' ? null : trimVal; - if (row !== 0 && (columns[col].isDatetime || columns[col].isNumeric)) { - columns[col].mixed = true; - } - } - } - } - - // If strings are intermixed with numbers or dates in a parsed column, it is an indication - // that parsing went wrong or the data was not intended to display as numbers or dates and - // parsing is too aggressive. Fall back to categories. Demonstrated in the - // highcharts/demo/column-drilldown sample. - if (isXColumn && columns[col].mixed) { - columns[col] = rawColumns[col]; - } - } - - // If the 0 column is date and descending, reverse all columns. - // TODO: probably this should apply to xColumns, not 0 column alone. - if (columns[0].isDatetime && descending) { - hasHeaderRow = typeof columns[0][0] !== 'number'; - for (col = 0; col < columns.length; col++) { - columns[col].reverse(); - if (hasHeaderRow) { - columns[col].unshift(columns[col].pop()); - } - } - } - }, - - /** - * A collection of available date formats, extendable from the outside to support - * custom date formats. - */ - dateFormats: { - 'YYYY-mm-dd': { - regex: /^([0-9]{4})[\-\/\.]([0-9]{2})[\-\/\.]([0-9]{2})$/, - parser: function (match) { - return Date.UTC(+match[1], match[2] - 1, +match[3]); - } - }, - 'dd/mm/YYYY': { - regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/, - parser: function (match) { - return Date.UTC(+match[3], match[2] - 1, +match[1]); - }, - alternative: 'mm/dd/YYYY' // different format with the same regex - }, - 'mm/dd/YYYY': { - regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/, - parser: function (match) { - return Date.UTC(+match[3], match[1] - 1, +match[2]); - } - }, - 'dd/mm/YY': { - regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/, - parser: function (match) { - return Date.UTC(+match[3] + 2000, match[2] - 1, +match[1]); - }, - alternative: 'mm/dd/YY' // different format with the same regex - }, - 'mm/dd/YY': { - regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/, - parser: function (match) { - console.log(match) - return Date.UTC(+match[3] + 2000, match[1] - 1, +match[2]); - } - } - }, - - /** - * Parse a date and return it as a number. Overridable through options.parseDate. - */ - parseDate: function (val) { - var parseDate = this.options.parseDate, - ret, - key, - format, - dateFormat = this.options.dateFormat || this.dateFormat, - match; - - if (parseDate) { - ret = parseDate(val); - } - - if (typeof val === 'string') { - // Auto-detect the date format the first time - if (!dateFormat) { - for (key in this.dateFormats) { - format = this.dateFormats[key]; - match = val.match(format.regex); - if (match) { - this.dateFormat = dateFormat = key; - this.alternativeFormat = format.alternative; - ret = format.parser(match); - break; - } - } - // Next time, use the one previously found - } else { - format = this.dateFormats[dateFormat]; - match = val.match(format.regex); - if (match) { - ret = format.parser(match); - } - } - // Fall back to Date.parse - if (!match) { - match = Date.parse(val); - // External tools like Date.js and MooTools extend Date object and - // returns a date. - if (typeof match === 'object' && match !== null && match.getTime) { - ret = match.getTime() - match.getTimezoneOffset() * 60000; - - // Timestamp - } else if (typeof match === 'number' && !isNaN(match)) { - ret = match - (new Date(match)).getTimezoneOffset() * 60000; - } - } - } - return ret; - }, - - /** - * Reorganize rows into columns - */ - rowsToColumns: function (rows) { - var row, - rowsLength, - col, - colsLength, - columns; - - if (rows) { - columns = []; - rowsLength = rows.length; - for (row = 0; row < rowsLength; row++) { - colsLength = rows[row].length; - for (col = 0; col < colsLength; col++) { - if (!columns[col]) { - columns[col] = []; - } - columns[col][row] = rows[row][col]; - } - } - } - return columns; - }, - - /** - * A hook for working directly on the parsed columns - */ - parsed: function () { - if (this.options.parsed) { - return this.options.parsed.call(this, this.columns); - } - }, - - getFreeIndexes: function (numberOfColumns, seriesBuilders) { - var s, - i, - freeIndexes = [], - freeIndexValues = [], - referencedIndexes; - - // Add all columns as free - for (i = 0; i < numberOfColumns; i = i + 1) { - freeIndexes.push(true); - } - - // Loop all defined builders and remove their referenced columns - for (s = 0; s < seriesBuilders.length; s = s + 1) { - referencedIndexes = seriesBuilders[s].getReferencedColumnIndexes(); - - for (i = 0; i < referencedIndexes.length; i = i + 1) { - freeIndexes[referencedIndexes[i]] = false; - } - } - - // Collect the values for the free indexes - for (i = 0; i < freeIndexes.length; i = i + 1) { - if (freeIndexes[i]) { - freeIndexValues.push(i); - } - } - - return freeIndexValues; - }, - - /** - * If a complete callback function is provided in the options, interpret the - * columns into a Highcharts options object. - */ - complete: function () { - - var columns = this.columns, - xColumns = [], - type, - options = this.options, - series, - data, - i, - j, - r, - seriesIndex, - chartOptions, - allSeriesBuilders = [], - builder, - freeIndexes, - typeCol, - index; - - xColumns.length = columns.length; - if (options.complete || options.afterComplete) { - - // Get the names and shift the top row - for (i = 0; i < columns.length; i++) { - if (this.headerRow === 0) { - columns[i].name = columns[i].shift(); - } - } - - // Use the next columns for series - series = []; - freeIndexes = this.getFreeIndexes(columns.length, this.valueCount.seriesBuilders); - - // Populate defined series - for (seriesIndex = 0; seriesIndex < this.valueCount.seriesBuilders.length; seriesIndex++) { - builder = this.valueCount.seriesBuilders[seriesIndex]; - - // If the builder can be populated with remaining columns, then add it to allBuilders - if (builder.populateColumns(freeIndexes)) { - allSeriesBuilders.push(builder); - } - } - - // Populate dynamic series - while (freeIndexes.length > 0) { - builder = new SeriesBuilder(); - builder.addColumnReader(0, 'x'); - - // Mark index as used (not free) - index = inArray(0, freeIndexes); - if (index !== -1) { - freeIndexes.splice(index, 1); - } - - for (i = 0; i < this.valueCount.global; i++) { - // Create and add a column reader for the next free column index - builder.addColumnReader(undefined, this.valueCount.globalPointArrayMap[i]); - } - - // If the builder can be populated with remaining columns, then add it to allBuilders - if (builder.populateColumns(freeIndexes)) { - allSeriesBuilders.push(builder); - } - } - - // Get the data-type from the first series x column - if (allSeriesBuilders.length > 0 && allSeriesBuilders[0].readers.length > 0) { - typeCol = columns[allSeriesBuilders[0].readers[0].columnIndex]; - if (typeCol !== undefined) { - if (typeCol.isDatetime) { - type = 'datetime'; - } else if (!typeCol.isNumeric) { - type = 'category'; - } - } - } - // Axis type is category, then the "x" column should be called "name" - if (type === 'category') { - for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) { - builder = allSeriesBuilders[seriesIndex]; - for (r = 0; r < builder.readers.length; r++) { - if (builder.readers[r].configName === 'x') { - builder.readers[r].configName = 'name'; - } - } - } - } - - // Read data for all builders - for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) { - builder = allSeriesBuilders[seriesIndex]; - - // Iterate down the cells of each column and add data to the series - data = []; - for (j = 0; j < columns[0].length; j++) { // TODO: which column's length should we use here - data[j] = builder.read(columns, j); - } - - // Add the series - series[seriesIndex] = { - data: data - }; - if (builder.name) { - series[seriesIndex].name = builder.name; - } - } - - - - // Do the callback - chartOptions = { - xAxis: { - type: type - }, - series: series - }; - if (options.complete) { - options.complete(chartOptions); - } - - // The afterComplete hook is used internally to avoid conflict with the externally - // available complete option. - if (options.afterComplete) { - options.afterComplete(chartOptions); - } - } - } - }); - - // Register the Data prototype and data function on Highcharts - Highcharts.Data = Data; - Highcharts.data = function (options, chartOptions) { - return new Data(options, chartOptions); - }; - - // Extend Chart.init so that the Chart constructor accepts a new configuration - // option group, data. - Highcharts.wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, callback) { - var chart = this; - - if (userOptions && userOptions.data) { - Highcharts.data(Highcharts.extend(userOptions.data, { - - afterComplete: function (dataOptions) { - var i, series; - - // Merge series configs - if (userOptions.hasOwnProperty('series')) { - if (typeof userOptions.series === 'object') { - i = Math.max(userOptions.series.length, dataOptions.series.length); - while (i--) { - series = userOptions.series[i] || {}; - userOptions.series[i] = Highcharts.merge(series, dataOptions.series[i]); - } - } else { // Allow merging in dataOptions.series (#2856) - delete userOptions.series; - } - } - - // Do the merge - userOptions = Highcharts.merge(dataOptions, userOptions); - - proceed.call(chart, userOptions, callback); - } - }), userOptions); - } else { - proceed.call(chart, userOptions, callback); - } - }); - - /** - * Creates a new SeriesBuilder. A SeriesBuilder consists of a number - * of ColumnReaders that reads columns and give them a name. - * Ex: A series builder can be constructed to read column 3 as 'x' and - * column 7 and 8 as 'y1' and 'y2'. - * The output would then be points/rows of the form {x: 11, y1: 22, y2: 33} - * - * The name of the builder is taken from the second column. In the above - * example it would be the column with index 7. - * @constructor - */ - SeriesBuilder = function () { - this.readers = []; - this.pointIsArray = true; - }; - - /** - * Populates readers with column indexes. A reader can be added without - * a specific index and for those readers the index is taken sequentially - * from the free columns (this is handled by the ColumnCursor instance). - * @returns {boolean} - */ - SeriesBuilder.prototype.populateColumns = function (freeIndexes) { - var builder = this, - enoughColumns = true; - - // Loop each reader and give it an index if its missing. - // The freeIndexes.shift() will return undefined if there - // are no more columns. - each(builder.readers, function (reader) { - if (reader.columnIndex === undefined) { - reader.columnIndex = freeIndexes.shift(); - } - }); - - // Now, all readers should have columns mapped. If not - // then return false to signal that this series should - // not be added. - each(builder.readers, function (reader) { - if (reader.columnIndex === undefined) { - enoughColumns = false; - } - }); - - return enoughColumns; - }; - - /** - * Reads a row from the dataset and returns a point or array depending - * on the names of the readers. - * @param columns - * @param rowIndex - * @returns {Array | Object} - */ - SeriesBuilder.prototype.read = function (columns, rowIndex) { - var builder = this, - pointIsArray = builder.pointIsArray, - point = pointIsArray ? [] : {}, - columnIndexes; - - // Loop each reader and ask it to read its value. - // Then, build an array or point based on the readers names. - each(builder.readers, function (reader) { - var value = columns[reader.columnIndex][rowIndex]; - if (pointIsArray) { - point.push(value); - } else { - point[reader.configName] = value; - } - }); - - // The name comes from the first column (excluding the x column) - if (this.name === undefined && builder.readers.length >= 2) { - columnIndexes = builder.getReferencedColumnIndexes(); - if (columnIndexes.length >= 2) { - // remove the first one (x col) - columnIndexes.shift(); - - // Sort the remaining - columnIndexes.sort(); - - // Now use the lowest index as name column - this.name = columns[columnIndexes.shift()].name; - } - } - - return point; - }; - - /** - * Creates and adds ColumnReader from the given columnIndex and configName. - * ColumnIndex can be undefined and in that case the reader will be given - * an index when columns are populated. - * @param columnIndex {Number | undefined} - * @param configName - */ - SeriesBuilder.prototype.addColumnReader = function (columnIndex, configName) { - this.readers.push({ - columnIndex: columnIndex, - configName: configName - }); - - if (!(configName === 'x' || configName === 'y' || configName === undefined)) { - this.pointIsArray = false; - } - }; - - /** - * Returns an array of column indexes that the builder will use when - * reading data. - * @returns {Array} - */ - SeriesBuilder.prototype.getReferencedColumnIndexes = function () { - var i, - referencedColumnIndexes = [], - columnReader; - - for (i = 0; i < this.readers.length; i = i + 1) { - columnReader = this.readers[i]; - if (columnReader.columnIndex !== undefined) { - referencedColumnIndexes.push(columnReader.columnIndex); - } - } - - return referencedColumnIndexes; - }; - - /** - * Returns true if the builder has a reader for the given configName. - * @param configName - * @returns {boolean} - */ - SeriesBuilder.prototype.hasReader = function (configName) { - var i, columnReader; - for (i = 0; i < this.readers.length; i = i + 1) { - columnReader = this.readers[i]; - if (columnReader.configName === configName) { - return true; - } - } - // Else return undefined - }; - - - -}(Highcharts)); |