highstock-rails 1.3.10 → 2.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }));