highstock-rails 1.3.10 → 2.1.10

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