first commit
This commit is contained in:
44
high/highcharts/js/modules/accessibility.js
Normal file
44
high/highcharts/js/modules/accessibility.js
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
Accessibility module
|
||||
|
||||
(c) 2010-2016 Highsoft AS
|
||||
Author: Oystein Moseng
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(k){"object"===typeof module&&module.exports?module.exports=k:k(Highcharts)})(function(k){(function(e){function k(a){for(var b=a.childNodes.length;b--;)a.appendChild(a.childNodes[b])}function t(a){var b;a&&a.onclick&&(b=m.createEvent("Events"),b.initEvent("click",!0,!1),a.onclick(b))}var w=e.win,m=w.document,h=e.each,y=e.erase,v=e.addEvent,z=e.removeEvent,A=e.fireEvent,B=e.dateFormat,u=e.merge,p={"default":["series","data point","data points"],line:["line","data point","data points"],spline:["line",
|
||||
"data point","data points"],area:["line","data point","data points"],areaspline:["line","data point","data points"],pie:["pie","slice","slices"],column:["column series","column","columns"],bar:["bar series","bar","bars"],scatter:["scatter series","data point","data points"],boxplot:["boxplot series","box","boxes"],arearange:["arearange series","data point","data points"],areasplinerange:["areasplinerange series","data point","data points"],bubble:["bubble series","bubble","bubbles"],columnrange:["columnrange series",
|
||||
"column","columns"],errorbar:["errorbar series","errorbar","errorbars"],funnel:["funnel","data point","data points"],pyramid:["pyramid","data point","data points"],waterfall:["waterfall series","column","columns"],map:["map","area","areas"],mapline:["line","data point","data points"],mappoint:["point series","data point","data points"],mapbubble:["bubble series","bubble","bubbles"]},C={boxplot:" Box plot charts are typically used to display groups of statistical data. Each data point in the chart can have up to 5 values: minimum, lower quartile, median, upper quartile and maximum. ",
|
||||
arearange:" Arearange charts are line charts displaying a range between a lower and higher value for each point. ",areasplinerange:" These charts are line charts displaying a range between a lower and higher value for each point. ",bubble:" Bubble charts are scatter charts where each data point also has a size value. ",columnrange:" Columnrange charts are column charts displaying a range between a lower and higher value for each point. ",errorbar:" Errorbar series are used to display the variability of the data. ",
|
||||
funnel:" Funnel charts are used to display reduction of data in stages. ",pyramid:" Pyramid charts consist of a single pyramid with item heights corresponding to each point value. ",waterfall:" A waterfall chart is a column chart where each column contributes towards a total end value. "},D="name id category x value y".split(" "),x="z open high q3 median q1 low close".split(" ");e.setOptions({accessibility:{enabled:!0,pointDescriptionThreshold:30,keyboardNavigation:{enabled:!0}}});e.wrap(e.Series.prototype,
|
||||
"render",function(a){a.apply(this,Array.prototype.slice.call(arguments,1));this.chart.options.accessibility.enabled&&this.setA11yDescription()});e.Series.prototype.setA11yDescription=function(){var a=this.chart.options.accessibility,b=this.points&&this.points[0].graphic&&this.points[0].graphic.element,d=b&&b.parentNode||this.graph&&this.graph.element||this.group&&this.group.element;d&&(d.lastChild===b&&k(d),this.points&&(this.points.length<a.pointDescriptionThreshold||!1===a.pointDescriptionThreshold)&&
|
||||
h(this.points,function(c){c.graphic&&(c.graphic.element.setAttribute("role","img"),c.graphic.element.setAttribute("tabindex","-1"),c.graphic.element.setAttribute("aria-label",a.pointDescriptionFormatter&&a.pointDescriptionFormatter(c)||c.buildPointInfoString()))}),1<this.chart.series.length||a.describeSingleSeries)&&(d.setAttribute("role","region"),d.setAttribute("tabindex","-1"),d.setAttribute("aria-label",a.seriesDescriptionFormatter&&a.seriesDescriptionFormatter(this)||this.buildSeriesInfoString()))};
|
||||
e.Series.prototype.buildSeriesInfoString=function(){var a=p[this.type]||p["default"],b=this.description||this.options.description;return(this.name?this.name+", ":"")+(1===this.chart.types.length?a[0]:"series")+" "+(this.index+1)+" of "+this.chart.series.length+(1===this.chart.types.length?" with ":". "+a[0]+" with ")+(this.points.length+" "+(1===this.points.length?a[1]:a[2]))+(b?". "+b:"")+(1<this.chart.yAxis.length&&this.yAxis?". Y axis, "+this.yAxis.getDescription():"")+(1<this.chart.xAxis.length&&
|
||||
this.xAxis?". X axis, "+this.xAxis.getDescription():"")};e.Point.prototype.buildPointInfoString=function(){var a=this,b=a.series,d=b.chart.options.accessibility,c="",f=!1,g=b.xAxis&&b.xAxis.isDatetimeAxis,b=g&&B(d.pointDateFormatter&&d.pointDateFormatter(a)||d.pointDateFormat||e.Tooltip.prototype.getXDateFormat(a,b.chart.options.tooltip,b.xAxis),a.x);h(x,function(c){void 0!==a[c]&&(f=!0)});f?(g&&(c=b),h(D.concat(x),function(b){void 0===a[b]||g&&"x"===b||(c+=(c?". ":"")+b+", "+this[b])})):c=(this.name||
|
||||
b||this.category||this.id||"x, "+this.x)+", "+(void 0!==this.value?this.value:this.y);return this.index+1+". "+c+"."+(this.description?" "+this.description:"")};e.Axis.prototype.getDescription=function(){return this.userOptions&&this.userOptions.description||this.axisTitle&&this.axisTitle.textStr||this.options.id||this.categories&&"categories"||"values"};e.Axis.prototype.panStep=function(a,b){var d=b||3,c=this.getExtremes(),f=(c.max-c.min)/d*a,d=c.max+f,f=c.min+f,g=d-f;0>a&&f<c.dataMin?(f=c.dataMin,
|
||||
d=f+g):0<a&&d>c.dataMax&&(d=c.dataMax,f=d-g);this.setExtremes(f,d)};e.wrap(e.Series.prototype,"init",function(a){a.apply(this,Array.prototype.slice.call(arguments,1));var b=this.chart;b.options.accessibility.enabled&&(b.types=b.types||[],0>b.types.indexOf(this.type)&&b.types.push(this.type),v(this,"remove",function(){var a=this,c=!1;h(b.series,function(f){f!==a&&0>b.types.indexOf(a.type)&&(c=!0)});c||y(b.types,a.type)}))});e.Chart.prototype.getTypeDescription=function(){var a=this.types&&this.types[0],
|
||||
b=this.series[0]&&this.series[0].mapTitle;if(a){if("map"===a)return b?"Map of "+b:"Map of unspecified region.";if(1<this.types.length)return"Combination chart.";if(-1<["spline","area","areaspline"].indexOf(a))return"Line chart."}else return"Empty chart.";return a+" chart."+(C[a]||"")};e.Chart.prototype.getAxesDescription=function(){var a=this.xAxis.length,b=this.yAxis.length,d={},c;if(a)if(d.xAxis="The chart has "+a+(1<a?" X axes":" X axis")+" displaying ",2>a)d.xAxis+=this.xAxis[0].getDescription()+
|
||||
".";else{for(c=0;c<a-1;++c)d.xAxis+=(c?", ":"")+this.xAxis[c].getDescription();d.xAxis+=" and "+this.xAxis[c].getDescription()+"."}if(b)if(d.yAxis="The chart has "+b+(1<b?" Y axes":" Y axis")+" displaying ",2>b)d.yAxis+=this.yAxis[0].getDescription()+".";else{for(c=0;c<b-1;++c)d.yAxis+=(c?", ":"")+this.yAxis[c].getDescription();d.yAxis+=" and "+this.yAxis[c].getDescription()+"."}return d};e.Chart.prototype.addAccessibleContextMenuAttribs=function(){var a=this.exportDivElements;a&&(h(a,function(a){"DIV"!==
|
||||
a.tagName||a.children&&a.children.length||(a.setAttribute("role","menuitem"),a.setAttribute("tabindex",-1))}),a[0].parentNode.setAttribute("role","menu"),a[0].parentNode.setAttribute("aria-label","Chart export"))};e.Point.prototype.highlight=function(){var a=this.series.chart;this.graphic&&this.graphic.element.focus&&this.graphic.element.focus();this.isNull?a.tooltip.hide(0):(this.onMouseOver(),a.tooltip.refresh(a.tooltip.shared?[this]:this));a.highlightedPoint=this;return this};e.Chart.prototype.highlightAdjacentPoint=
|
||||
function(a){var b=this.series,d=this.highlightedPoint;if(!b[0]||!b[0].points)return!1;if(!d)return b[0].points[0].highlight();b=b[d.series.index+(a?1:-1)];d=a?d.series.points[d.index+1]||b&&b.points[0]:d.series.points[d.index-1]||b&&b.points[b.points.length-1];return void 0===d?!1:d.isNull&&this.options.accessibility.keyboardNavigation&&this.options.accessibility.keyboardNavigation.skipNullPoints?(this.highlightedPoint=d,this.highlightAdjacentPoint(a)):d.highlight()};e.Chart.prototype.showExportMenu=
|
||||
function(){this.exportSVGElements&&this.exportSVGElements[0]&&(this.exportSVGElements[0].element.onclick(),this.highlightExportItem(0))};e.Chart.prototype.highlightExportItem=function(a){var b=this.exportDivElements&&this.exportDivElements[a],d=this.exportDivElements&&this.exportDivElements[this.highlightedExportItem];if(b&&"DIV"===b.tagName&&(!b.children||!b.children.length)){b.focus&&b.focus();if(d&&d.onmouseout)d.onmouseout();if(b.onmouseover)b.onmouseover();this.highlightedExportItem=a;return!0}};
|
||||
e.Chart.prototype.highlightRangeSelectorButton=function(a){var b=this.rangeSelector.buttons;b[this.highlightedRangeSelectorItemIx]&&b[this.highlightedRangeSelectorItemIx].setState(this.oldRangeSelectorItemState||0);this.highlightedRangeSelectorItemIx=a;return b[a]?(b[a].element.focus&&b[a].element.focus(),this.oldRangeSelectorItemState=b[a].state,b[a].setState(2),!0):!1};e.Chart.prototype.hideExportMenu=function(){var a=this.exportDivElements;if(a){h(a,function(a){A(a,"mouseleave")});if(a[this.highlightedExportItem]&&
|
||||
a[this.highlightedExportItem].onmouseout)a[this.highlightedExportItem].onmouseout();this.highlightedExportItem=0;this.renderTo.focus()}};e.Chart.prototype.addKeyboardNavEvents=function(){function a(c){this.keyCodeMap=c.keyCodeMap;this.move=c.move;this.validate=c.validate;this.init=c.init;this.transformTabs=!1!==c.transformTabs}function b(b,g){return new a(u({keyCodeMap:b,move:function(a){c.keyboardNavigationModuleIndex+=a;var b=c.keyboardNavigationModules[c.keyboardNavigationModuleIndex];if(b){if(b.validate&&
|
||||
!b.validate())return this.move(a);if(b.init)return b.init(a),!0}c.keyboardNavigationModuleIndex=0;c.slipNextTab=!0;return!1}},g))}function d(a){a=a||w.event;var b=c.keyboardNavigationModules[c.keyboardNavigationModuleIndex];9===(a.which||a.keyCode)&&c.slipNextTab?c.slipNextTab=!1:(c.slipNextTab=!1,b&&b.run(a)&&a.preventDefault())}var c=this;a.prototype={run:function(c){var a=this,b=c.which||c.keyCode,d=!1,b=this.transformTabs&&9===b?c.shiftKey?37:39:b;h(this.keyCodeMap,function(e){-1<e[0].indexOf(b)&&
|
||||
(d=!1===e[1].call(a,b,c)?!1:!0)});return d}};c.keyboardNavigationModules=[b([[[37,39],function(a){if(!c.highlightAdjacentPoint(39===a))return this.move(39===a?1:-1)}],[[38,40],function(a){var b;if(c.highlightedPoint)if((b=c.series[c.highlightedPoint.series.index+(38===a?-1:1)])&&b.points[0])b.points[0].highlight();else return this.move(40===a?1:-1)}],[[13,32],function(){c.highlightedPoint&&c.highlightedPoint.firePointEvent("click")}]],{init:function(a){var b=c.series&&c.series[c.series.length-1],
|
||||
b=b&&b.points&&b.points[b.points.length-1];0>a&&b&&b.highlight()}}),b([[[37,38],function(){for(var a=c.highlightedExportItem||0,b=!0,d=c.series;a--;)if(c.highlightExportItem(a)){b=!1;break}if(b)return c.hideExportMenu(),d&&d.length&&(a=d[d.length-1],a.points.length&&a.points[a.points.length-1].highlight()),this.move(-1)}],[[39,40],function(){for(var a=!0,b=(c.highlightedExportItem||0)+1;b<c.exportDivElements.length;++b)if(c.highlightExportItem(b)){a=!1;break}if(a)return c.hideExportMenu(),this.move(1)}],
|
||||
[[13,32],function(){t(c.exportDivElements[c.highlightedExportItem])}]],{validate:function(){return c.exportChart&&!(c.options.exporting&&!1===c.options.exporting.enabled)},init:function(a){c.highlightedPoint=null;c.showExportMenu();if(0>a&&c.exportDivElements)for(a=c.exportDivElements.length;-1<a&&!c.highlightExportItem(a);--a);}}),b([[[38,40,37,39],function(a){c[38===a||40===a?"yAxis":"xAxis"][0].panStep(39>a?-1:1)}],[[9],function(a,b){var d;c.mapNavButtons[c.focusedMapNavButtonIx].setState(0);if(b.shiftKey&&
|
||||
!c.focusedMapNavButtonIx||!b.shiftKey&&c.focusedMapNavButtonIx)return c.mapZoom(),this.move(b.shiftKey?-1:1);c.focusedMapNavButtonIx+=b.shiftKey?-1:1;d=c.mapNavButtons[c.focusedMapNavButtonIx];d.element.focus&&d.element.focus();d.setState(2)}],[[13,32],function(){t(c.mapNavButtons[c.focusedMapNavButtonIx].element)}]],{validate:function(){return c.mapZoom&&c.mapNavButtons&&2===c.mapNavButtons.length},transformTabs:!1,init:function(a){var b=c.mapNavButtons[0],d=c.mapNavButtons[1],b=0<a?b:d;h(c.mapNavButtons,
|
||||
function(a,c){a.element.setAttribute("tabindex",-1);a.element.setAttribute("role","button");a.element.setAttribute("aria-label","Zoom "+(c?"out":"")+"chart")});b.element.focus&&b.element.focus();b.setState(2);c.focusedMapNavButtonIx=0<a?0:1}}),b([[[37,39,38,40],function(a){a=37===a||38===a?-1:1;if(!c.highlightRangeSelectorButton(c.highlightedRangeSelectorItemIx+a))return this.move(a)}],[[13,32],function(){3!==c.oldRangeSelectorItemState&&t(c.rangeSelector.buttons[c.highlightedRangeSelectorItemIx].element)}]],
|
||||
{validate:function(){return c.rangeSelector&&c.rangeSelector.buttons&&c.rangeSelector.buttons.length},init:function(a){h(c.rangeSelector.buttons,function(a){a.element.setAttribute("tabindex","-1");a.element.setAttribute("role","button");a.element.setAttribute("aria-label","Select range "+(a.text&&a.text.textStr))});c.highlightRangeSelectorButton(0<a?0:c.rangeSelector.buttons.length-1)}}),b([[[9,38,40],function(a,b){var d=9===a&&b.shiftKey||38===a?-1:1,e=c.highlightedInputRangeIx+=d;if(1<e||0>e)return this.move(d);
|
||||
c.rangeSelector[e?"maxInput":"minInput"].focus()}]],{validate:function(){return c.rangeSelector&&!1!==c.options.rangeSelector.inputEnabled&&c.rangeSelector.minInput&&c.rangeSelector.maxInput},transformTabs:!1,init:function(a){h(["minInput","maxInput"],function(a,b){c.rangeSelector[a]&&(c.rangeSelector[a].setAttribute("tabindex","-1"),c.rangeSelector[a].setAttribute("role","textbox"),c.rangeSelector[a].setAttribute("aria-label","Select "+(b?"end":"start")+" date."))});c.highlightedInputRangeIx=0<a?
|
||||
0:1;c.rangeSelector[c.highlightedInputRangeIx?"maxInput":"minInput"].focus()}})];c.keyboardNavigationModuleIndex=0;c.renderTo.tabIndex||c.renderTo.setAttribute("tabindex","0");v(c.renderTo,"keydown",d);v(c,"destroy",function(){z(c.renderTo,"keydown",d)})};e.Chart.prototype.addScreenReaderRegion=function(a){var b=this,d=b.series,c=b.options,e=c.accessibility,g=b.screenReaderRegion=m.createElement("div"),h=m.createElement("h3"),q=m.createElement("a"),r=m.createElement("h3"),k={position:"absolute",left:"-9999px",
|
||||
top:"auto",width:"1px",height:"1px",overflow:"hidden"},l=b.types||[],l=(1===l.length&&"pie"===l[0]||"map"===l[0])&&{}||b.getAxesDescription(),n=d[0]&&p[d[0].type]||p["default"];g.setAttribute("role","region");g.setAttribute("aria-label","Chart screen reader information.");g.innerHTML=e.screenReaderSectionFormatter&&e.screenReaderSectionFormatter(b)||'<div tabindex="0">Use regions/landmarks to skip ahead to chart'+(1<d.length?" and navigate between data series":"")+".</div><h3>Summary.</h3><div>"+
|
||||
(c.title.text||"Chart")+(c.subtitle&&c.subtitle.text?". "+c.subtitle.text:"")+"</div><h3>Long description.</h3><div>"+(c.chart.description||"No description available.")+"</div><h3>Structure.</h3><div>Chart type: "+(c.chart.typeDescription||b.getTypeDescription())+"</div>"+(1===d.length?"<div>"+n[0]+" with "+d[0].points.length+" "+(1===d[0].points.length?n[1]:n[2])+".</div>":"")+(l.xAxis?"<div>"+l.xAxis+"</div>":"")+(l.yAxis?"<div>"+l.yAxis+"</div>":"");b.getCSV&&(q.innerHTML="View as data table.",
|
||||
q.href="#"+a,q.setAttribute("tabindex","-1"),q.onclick=e.onTableAnchorClick||function(){b.viewData();m.getElementById(a).focus()},h.appendChild(q),g.appendChild(h));r.innerHTML="Chart graphic.";b.renderTo.insertBefore(r,b.renderTo.firstChild);b.renderTo.insertBefore(g,b.renderTo.firstChild);u(!0,r.style,k);u(!0,g.style,k)};e.Chart.prototype.callbacks.push(function(a){var b=a.options,d=b.accessibility;if(d.enabled){var c=m.createElementNS("http://www.w3.org/2000/svg","title"),f=m.createElementNS("http://www.w3.org/2000/svg",
|
||||
"g"),g=a.container.getElementsByTagName("desc")[0],k=a.container.getElementsByTagName("text"),q="highcharts-title-"+a.index,r="highcharts-data-table-"+a.index,p=b.title.text||"Chart",l=b.exporting&&b.exporting.csv&&b.exporting.csv.columnHeaderFormatter,n=[];c.textContent=p;c.id=q;g.parentNode.insertBefore(c,g);a.renderTo.setAttribute("role","region");a.renderTo.setAttribute("aria-label",p+". Use up and down arrows to navigate.");if(a.exportSVGElements&&a.exportSVGElements[0]&&a.exportSVGElements[0].element){var t=
|
||||
a.exportSVGElements[0].element.onclick,c=a.exportSVGElements[0].element.parentNode;a.exportSVGElements[0].element.onclick=function(){t.apply(this,Array.prototype.slice.call(arguments));a.addAccessibleContextMenuAttribs();a.highlightExportItem(0)};a.exportSVGElements[0].element.setAttribute("role","button");a.exportSVGElements[0].element.setAttribute("aria-label","View export menu");f.appendChild(a.exportSVGElements[0].element);f.setAttribute("role","region");f.setAttribute("aria-label","Chart export menu");
|
||||
c.appendChild(f)}h(k,function(a){a.setAttribute("aria-hidden","true")});a.addScreenReaderRegion(r);d.keyboardNavigation&&a.addKeyboardNavEvents();u(!0,b.exporting,{csv:{columnHeaderFormatter:function(a,c,b){var d=n[n.length-1];1<b&&(d&&d.text)!==a.name&&n.push({text:a.name,span:b});return l?l.call(this,a,c,b):1<b?c:a.name}}});e.wrap(a,"getTable",function(a){return a.apply(this,Array.prototype.slice.call(arguments,1)).replace("<table>",'<table id="'+r+'" summary="Table representation of chart"><caption>'+
|
||||
p+"</caption>")});e.wrap(a,"viewData",function(a){if(!this.insertedTable){a.apply(this,Array.prototype.slice.call(arguments,1));var c=m.getElementById(r),b=c.getElementsByTagName("tbody")[0],d=b.firstChild.children,e="<tr><td></td>",f,g;c.setAttribute("tabindex","-1");h(b.children,function(a){f=a.firstChild;g=m.createElement("th");g.setAttribute("scope","row");g.innerHTML=f.innerHTML;f.parentNode.replaceChild(g,f)});h(d,function(a){"TH"===a.tagName&&a.setAttribute("scope","col")});n.length&&(h(n,
|
||||
function(a){e+='<th scope="col" colspan="'+a.span+'">'+a.text+"</th>"}),b.insertAdjacentHTML("afterbegin",e))}})}})})(k)});
|
1005
high/highcharts/js/modules/accessibility.src.js
Normal file
1005
high/highcharts/js/modules/accessibility.src.js
Normal file
File diff suppressed because it is too large
Load Diff
14
high/highcharts/js/modules/annotations.js
Normal file
14
high/highcharts/js/modules/annotations.js
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
|
||||
(c) 2009-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(p){"object"===typeof module&&module.exports?module.exports=p:p(Highcharts)})(function(p){(function(h){var q=h.defined,k=h.isNumber,p=h.inArray,v=h.isArray,w=h.merge,C=h.Chart,x=h.extend,D=h.each,r,E;E=["path","rect","circle"];r={top:0,left:0,center:.5,middle:.5,bottom:1,right:1};var F=function(){this.init.apply(this,arguments)};F.prototype={init:function(a,d){var c=d.shape&&d.shape.type;this.chart=a;var b,f;f={xAxis:0,yAxis:0,title:{style:{},text:"",x:0,y:0},shape:{params:{stroke:"#000000",
|
||||
fill:"transparent",strokeWidth:2}}};b={circle:{params:{x:0,y:0}}};b[c]&&(f.shape=w(f.shape,b[c]));this.options=w({},f,d)},render:function(a){var d=this.chart,c=this.chart.renderer,b=this.group,f=this.title,e=this.shape,l=this.options,h=l.title,k=l.shape;b||(b=this.group=c.g());!e&&k&&-1!==p(k.type,E)&&(e=this.shape=c[l.shape.type](k.params),e.add(b));!f&&h&&(f=this.title=c.label(h),f.add(b));b.add(d.annotations.group);this.linkObjects();!1!==a&&this.redraw()},redraw:function(){var a=this.options,
|
||||
d=this.chart,c=this.group,b=this.title,f=this.shape,e=this.linkedObject,l=d.xAxis[a.xAxis],d=d.yAxis[a.yAxis],y=a.width,z=a.height,A=r[a.anchorY],B=r[a.anchorX],t,m,g,u;e&&(t=e instanceof h.Point?"point":e instanceof h.Series?"series":null,"point"===t?(a.xValue=e.x,a.yValue=e.y,m=e.series):"series"===t&&(m=e),c.visibility!==m.group.visibility&&c.attr({visibility:m.group.visibility}));e=q(a.xValue)?l.toPixels(a.xValue+l.minPointOffset)-l.minPixelPadding:a.x;m=q(a.yValue)?d.toPixels(a.yValue):a.y;if(k(e)&&
|
||||
k(m)){b&&(b.attr(a.title),b.css(a.title.style));if(f){b=x({},a.shape.params);if("values"===a.units){for(g in b)-1<p(g,["width","x"])?b[g]=l.translate(b[g]):-1<p(g,["height","y"])&&(b[g]=d.translate(b[g]));b.width&&(b.width-=l.toPixels(0)-l.left);b.x&&(b.x+=l.minPixelPadding);if("path"===a.shape.type){g=b.d;t=e;for(var v=m,w=g.length,n=0;n<w;)k(g[n])&&k(g[n+1])?(g[n]=l.toPixels(g[n])-t,g[n+1]=d.toPixels(g[n+1])-v,n+=2):n+=1}}"circle"===a.shape.type&&(b.x+=b.r,b.y+=b.r);f.attr(b)}c.bBox=null;k(y)||
|
||||
(u=c.getBBox(),y=u.width);k(z)||(u||(u=c.getBBox()),z=u.height);k(B)||(B=r.center);k(A)||(A=r.center);e-=y*B;m-=z*A;q(c.translateX)&&q(c.translateY)?c.animate({translateX:e,translateY:m}):c.translate(e,m)}},destroy:function(){var a=this,d=this.chart.annotations.allItems,c=d.indexOf(a);-1<c&&d.splice(c,1);D(["title","shape","group"],function(b){a[b]&&(a[b].destroy(),a[b]=null)});a.group=a.title=a.shape=a.chart=a.options=null},update:function(a,d){x(this.options,a);this.linkObjects();this.render(d)},
|
||||
linkObjects:function(){var a=this.chart,d=this.linkedObject,c=d&&(d.id||d.options.id),b=this.options.linkedTo;q(b)?q(d)&&b===c||(this.linkedObject=a.get(b)):this.linkedObject=null}};x(C.prototype,{annotations:{add:function(a,d){var c=this.allItems,b=this.chart,f,e;v(a)||(a=[a]);for(e=a.length;e--;)f=new F(b,a[e]),c.push(f),f.render(d)},redraw:function(){D(this.allItems,function(a){a.redraw()})}}});C.prototype.callbacks.push(function(a){var d=a.options.annotations,c;c=a.renderer.g("annotations");c.attr({zIndex:7});
|
||||
c.add();a.annotations.allItems=[];a.annotations.chart=a;a.annotations.group=c;v(d)&&0<d.length&&a.annotations.add(a.options.annotations);h.addEvent(a,"redraw",function(){a.annotations.redraw()})})})(p)});
|
408
high/highcharts/js/modules/annotations.src.js
Normal file
408
high/highcharts/js/modules/annotations.src.js
Normal file
@ -0,0 +1,408 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
*
|
||||
* (c) 2009-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* (c) 2009-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var defined = H.defined,
|
||||
isNumber = H.isNumber,
|
||||
inArray = H.inArray,
|
||||
isArray = H.isArray,
|
||||
merge = H.merge,
|
||||
Chart = H.Chart,
|
||||
extend = H.extend,
|
||||
each = H.each;
|
||||
|
||||
var ALIGN_FACTOR,
|
||||
ALLOWED_SHAPES;
|
||||
|
||||
ALLOWED_SHAPES = ['path', 'rect', 'circle'];
|
||||
|
||||
ALIGN_FACTOR = {
|
||||
top: 0,
|
||||
left: 0,
|
||||
center: 0.5,
|
||||
middle: 0.5,
|
||||
bottom: 1,
|
||||
right: 1
|
||||
};
|
||||
|
||||
function defaultOptions(shapeType) {
|
||||
var shapeOptions,
|
||||
options;
|
||||
|
||||
options = {
|
||||
xAxis: 0,
|
||||
yAxis: 0,
|
||||
title: {
|
||||
style: {},
|
||||
text: '',
|
||||
x: 0,
|
||||
y: 0
|
||||
},
|
||||
shape: {
|
||||
params: {
|
||||
stroke: '#000000',
|
||||
fill: 'transparent',
|
||||
strokeWidth: 2
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
shapeOptions = {
|
||||
circle: {
|
||||
params: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (shapeOptions[shapeType]) {
|
||||
options.shape = merge(options.shape, shapeOptions[shapeType]);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function translatePath(d, xAxis, yAxis, xOffset, yOffset) {
|
||||
var len = d.length,
|
||||
i = 0;
|
||||
|
||||
while (i < len) {
|
||||
if (isNumber(d[i]) && isNumber(d[i + 1])) {
|
||||
d[i] = xAxis.toPixels(d[i]) - xOffset;
|
||||
d[i + 1] = yAxis.toPixels(d[i + 1]) - yOffset;
|
||||
i += 2;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
// Define annotation prototype
|
||||
var Annotation = function() {
|
||||
this.init.apply(this, arguments);
|
||||
};
|
||||
Annotation.prototype = {
|
||||
/*
|
||||
* Initialize the annotation
|
||||
*/
|
||||
init: function(chart, options) {
|
||||
var shapeType = options.shape && options.shape.type;
|
||||
|
||||
this.chart = chart;
|
||||
this.options = merge({}, defaultOptions(shapeType), options);
|
||||
},
|
||||
|
||||
/*
|
||||
* Render the annotation
|
||||
*/
|
||||
render: function(redraw) {
|
||||
var annotation = this,
|
||||
chart = this.chart,
|
||||
renderer = annotation.chart.renderer,
|
||||
group = annotation.group,
|
||||
title = annotation.title,
|
||||
shape = annotation.shape,
|
||||
options = annotation.options,
|
||||
titleOptions = options.title,
|
||||
shapeOptions = options.shape;
|
||||
|
||||
if (!group) {
|
||||
group = annotation.group = renderer.g();
|
||||
}
|
||||
|
||||
|
||||
if (!shape && shapeOptions && inArray(shapeOptions.type, ALLOWED_SHAPES) !== -1) {
|
||||
shape = annotation.shape = renderer[options.shape.type](shapeOptions.params);
|
||||
shape.add(group);
|
||||
}
|
||||
|
||||
if (!title && titleOptions) {
|
||||
title = annotation.title = renderer.label(titleOptions);
|
||||
title.add(group);
|
||||
}
|
||||
|
||||
group.add(chart.annotations.group);
|
||||
|
||||
// link annotations to point or series
|
||||
annotation.linkObjects();
|
||||
|
||||
if (redraw !== false) {
|
||||
annotation.redraw();
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Redraw the annotation title or shape after options update
|
||||
*/
|
||||
redraw: function() {
|
||||
var options = this.options,
|
||||
chart = this.chart,
|
||||
group = this.group,
|
||||
title = this.title,
|
||||
shape = this.shape,
|
||||
linkedTo = this.linkedObject,
|
||||
xAxis = chart.xAxis[options.xAxis],
|
||||
yAxis = chart.yAxis[options.yAxis],
|
||||
width = options.width,
|
||||
height = options.height,
|
||||
anchorY = ALIGN_FACTOR[options.anchorY],
|
||||
anchorX = ALIGN_FACTOR[options.anchorX],
|
||||
shapeParams,
|
||||
linkType,
|
||||
series,
|
||||
param,
|
||||
bbox,
|
||||
x,
|
||||
y;
|
||||
|
||||
if (linkedTo) {
|
||||
linkType = (linkedTo instanceof H.Point) ? 'point' :
|
||||
(linkedTo instanceof H.Series) ? 'series' : null;
|
||||
|
||||
if (linkType === 'point') {
|
||||
options.xValue = linkedTo.x;
|
||||
options.yValue = linkedTo.y;
|
||||
series = linkedTo.series;
|
||||
} else if (linkType === 'series') {
|
||||
series = linkedTo;
|
||||
}
|
||||
|
||||
if (group.visibility !== series.group.visibility) {
|
||||
group.attr({
|
||||
visibility: series.group.visibility
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Based on given options find annotation pixel position
|
||||
x = (defined(options.xValue) ? xAxis.toPixels(options.xValue + xAxis.minPointOffset) - xAxis.minPixelPadding : options.x);
|
||||
y = defined(options.yValue) ? yAxis.toPixels(options.yValue) : options.y;
|
||||
|
||||
if (!isNumber(x) || !isNumber(y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (title) {
|
||||
title.attr(options.title);
|
||||
title.css(options.title.style);
|
||||
}
|
||||
|
||||
if (shape) {
|
||||
shapeParams = extend({}, options.shape.params);
|
||||
|
||||
if (options.units === 'values') {
|
||||
for (param in shapeParams) {
|
||||
if (inArray(param, ['width', 'x']) > -1) {
|
||||
shapeParams[param] = xAxis.translate(shapeParams[param]);
|
||||
} else if (inArray(param, ['height', 'y']) > -1) {
|
||||
shapeParams[param] = yAxis.translate(shapeParams[param]);
|
||||
}
|
||||
}
|
||||
|
||||
if (shapeParams.width) {
|
||||
shapeParams.width -= xAxis.toPixels(0) - xAxis.left;
|
||||
}
|
||||
|
||||
if (shapeParams.x) {
|
||||
shapeParams.x += xAxis.minPixelPadding;
|
||||
}
|
||||
|
||||
if (options.shape.type === 'path') {
|
||||
translatePath(shapeParams.d, xAxis, yAxis, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// move the center of the circle to shape x/y
|
||||
if (options.shape.type === 'circle') {
|
||||
shapeParams.x += shapeParams.r;
|
||||
shapeParams.y += shapeParams.r;
|
||||
}
|
||||
|
||||
shape.attr(shapeParams);
|
||||
}
|
||||
|
||||
group.bBox = null;
|
||||
|
||||
// If annotation width or height is not defined in options use bounding box size
|
||||
if (!isNumber(width)) {
|
||||
bbox = group.getBBox();
|
||||
width = bbox.width;
|
||||
}
|
||||
|
||||
if (!isNumber(height)) {
|
||||
// get bbox only if it wasn't set before
|
||||
if (!bbox) {
|
||||
bbox = group.getBBox();
|
||||
}
|
||||
|
||||
height = bbox.height;
|
||||
}
|
||||
|
||||
// Calculate anchor point
|
||||
if (!isNumber(anchorX)) {
|
||||
anchorX = ALIGN_FACTOR.center;
|
||||
}
|
||||
|
||||
if (!isNumber(anchorY)) {
|
||||
anchorY = ALIGN_FACTOR.center;
|
||||
}
|
||||
|
||||
// Translate group according to its dimension and anchor point
|
||||
x = x - width * anchorX;
|
||||
y = y - height * anchorY;
|
||||
|
||||
if (defined(group.translateX) && defined(group.translateY)) {
|
||||
group.animate({
|
||||
translateX: x,
|
||||
translateY: y
|
||||
});
|
||||
} else {
|
||||
group.translate(x, y);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Destroy the annotation
|
||||
*/
|
||||
destroy: function() {
|
||||
var annotation = this,
|
||||
chart = this.chart,
|
||||
allItems = chart.annotations.allItems,
|
||||
index = allItems.indexOf(annotation);
|
||||
|
||||
if (index > -1) {
|
||||
allItems.splice(index, 1);
|
||||
}
|
||||
|
||||
each(['title', 'shape', 'group'], function(element) {
|
||||
if (annotation[element]) {
|
||||
annotation[element].destroy();
|
||||
annotation[element] = null;
|
||||
}
|
||||
});
|
||||
|
||||
annotation.group = annotation.title = annotation.shape = annotation.chart = annotation.options = null;
|
||||
},
|
||||
|
||||
/*
|
||||
* Update the annotation with a given options
|
||||
*/
|
||||
update: function(options, redraw) {
|
||||
extend(this.options, options);
|
||||
|
||||
// update link to point or series
|
||||
this.linkObjects();
|
||||
|
||||
this.render(redraw);
|
||||
},
|
||||
|
||||
linkObjects: function() {
|
||||
var annotation = this,
|
||||
chart = annotation.chart,
|
||||
linkedTo = annotation.linkedObject,
|
||||
linkedId = linkedTo && (linkedTo.id || linkedTo.options.id),
|
||||
options = annotation.options,
|
||||
id = options.linkedTo;
|
||||
|
||||
if (!defined(id)) {
|
||||
annotation.linkedObject = null;
|
||||
} else if (!defined(linkedTo) || id !== linkedId) {
|
||||
annotation.linkedObject = chart.get(id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Add annotations methods to chart prototype
|
||||
extend(Chart.prototype, {
|
||||
annotations: {
|
||||
/*
|
||||
* Unified method for adding annotations to the chart
|
||||
*/
|
||||
add: function(options, redraw) {
|
||||
var annotations = this.allItems,
|
||||
chart = this.chart,
|
||||
item,
|
||||
len;
|
||||
|
||||
if (!isArray(options)) {
|
||||
options = [options];
|
||||
}
|
||||
|
||||
len = options.length;
|
||||
|
||||
while (len--) {
|
||||
item = new Annotation(chart, options[len]);
|
||||
annotations.push(item);
|
||||
item.render(redraw);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Redraw all annotations, method used in chart events
|
||||
*/
|
||||
redraw: function() {
|
||||
each(this.allItems, function(annotation) {
|
||||
annotation.redraw();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Initialize on chart load
|
||||
Chart.prototype.callbacks.push(function(chart) {
|
||||
var options = chart.options.annotations,
|
||||
group;
|
||||
|
||||
group = chart.renderer.g('annotations');
|
||||
group.attr({
|
||||
zIndex: 7
|
||||
});
|
||||
group.add();
|
||||
|
||||
// initialize empty array for annotations
|
||||
chart.annotations.allItems = [];
|
||||
|
||||
// link chart object to annotations
|
||||
chart.annotations.chart = chart;
|
||||
|
||||
// link annotations group element to the chart
|
||||
chart.annotations.group = group;
|
||||
|
||||
if (isArray(options) && options.length > 0) {
|
||||
chart.annotations.add(chart.options.annotations);
|
||||
}
|
||||
|
||||
// update annotations after chart redraw
|
||||
H.addEvent(chart, 'redraw', function() {
|
||||
chart.annotations.redraw();
|
||||
});
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
22
high/highcharts/js/modules/boost.js
Normal file
22
high/highcharts/js/modules/boost.js
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
Boost module
|
||||
|
||||
(c) 2010-2016 Highsoft AS
|
||||
Author: Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(n){"object"===typeof module&&module.exports?module.exports=n:n(Highcharts)})(function(n){(function(f){function n(a,b,c,e,h){h=h||0;e=e||5E4;for(var k=h+e,f=!0;f&&h<k&&h<a.length;)f=b(a[h],h),h+=1;f&&(h<a.length?setTimeout(function(){n(a,b,c,e,h)}):c&&c())}var x=f.win.document,U=function(){},V=f.Color,p=f.Series,d=f.seriesTypes,q=f.each,y=f.extend,W=f.addEvent,X=f.fireEvent,z=f.grep,u=f.isNumber,Y=f.merge,Z=f.pick,l=f.wrap,v=f.getOptions().plotOptions,F;q(["area","arearange","column","line",
|
||||
"scatter"],function(a){v[a]&&(v[a].boostThreshold=5E3)});q(["translate","generatePoints","drawTracker","drawPoints","render"],function(a){function b(b){var e=this.options.stacking&&("translate"===a||"generatePoints"===a);if((this.processedXData||this.options.data).length<(this.options.boostThreshold||Number.MAX_VALUE)||e)"render"===a&&this.image&&(this.image.attr({href:""}),this.animate=null),b.call(this);else if(this[a+"Canvas"])this[a+"Canvas"]()}l(p.prototype,a,b);"translate"===a&&(d.column&&l(d.column.prototype,
|
||||
a,b),d.arearange&&l(d.arearange.prototype,a,b))});l(p.prototype,"getExtremes",function(a){this.hasExtremes()||a.apply(this,Array.prototype.slice.call(arguments,1))});l(p.prototype,"setData",function(a){this.hasExtremes(!0)||a.apply(this,Array.prototype.slice.call(arguments,1))});l(p.prototype,"processData",function(a){this.hasExtremes(!0)||a.apply(this,Array.prototype.slice.call(arguments,1))});f.extend(p.prototype,{pointRange:0,allowDG:!1,hasExtremes:function(a){var b=this.options,c=this.xAxis&&
|
||||
this.xAxis.options,e=this.yAxis&&this.yAxis.options;return b.data.length>(b.boostThreshold||Number.MAX_VALUE)&&u(e.min)&&u(e.max)&&(!a||u(c.min)&&u(c.max))},destroyGraphics:function(){var a=this,b=this.points,c,e;if(b)for(e=0;e<b.length;e+=1)(c=b[e])&&c.graphic&&(c.graphic=c.graphic.destroy());q(["graph","area","tracker"],function(b){a[b]&&(a[b]=a[b].destroy())})},getContext:function(){var a=this.chart,b=a.plotWidth,c=a.plotHeight,e=this.ctx,h=function(a,b,c,e,h,f,d){a.call(this,c,b,e,h,f,d)};this.canvas?
|
||||
e.clearRect(0,0,b,c):(this.canvas=x.createElement("canvas"),this.image=a.renderer.image("",0,0,b,c).add(this.group),this.ctx=e=this.canvas.getContext("2d"),a.inverted&&q(["moveTo","lineTo","rect","arc"],function(a){l(e,a,h)}));this.canvas.width=b;this.canvas.height=c;this.image.attr({width:b,height:c});return e},canvasToSVG:function(){this.image.attr({href:this.canvas.toDataURL("image/png")})},cvsLineTo:function(a,b,c){a.lineTo(b,c)},renderCanvas:function(){var a=this,b=a.options,c=a.chart,e=this.xAxis,
|
||||
h=this.yAxis,k,d=0,l=a.processedXData,p=a.processedYData,q=b.data,m=e.getExtremes(),v=m.min,x=m.max,m=h.getExtremes(),z=m.min,aa=m.max,G={},A,ba=!!a.sampling,H,I=b.marker&&b.marker.radius,J=this.cvsDrawPoint,B=b.lineWidth?this.cvsLineTo:!1,K=1>=I?this.cvsMarkerSquare:this.cvsMarkerCircle,ca=!1!==b.enableMouseTracking,L,m=b.threshold,r=h.getThreshold(m),M=u(m),N=r,da=this.fill,O=a.pointArrayMap&&"low,high"===a.pointArrayMap.join(","),P=!!b.stacking,ea=a.cropStart||0,m=c.options.loading,fa=a.requireSorting,
|
||||
Q,ga=b.connectNulls,R=!l,C,D,t,w,ha=a.fillOpacity?(new V(a.color)).setOpacity(Z(b.fillOpacity,.75)).get():a.color,S=function(){da?(k.fillStyle=ha,k.fill()):(k.strokeStyle=a.color,k.lineWidth=b.lineWidth,k.stroke())},T=function(a,b,c){0===d&&(k.beginPath(),B&&(k.lineJoin="round"));Q?k.moveTo(a,b):J?J(k,a,b,c,L):B?B(k,a,b):K&&K(k,a,b,I);d+=1;1E3===d&&(S(),d=0);L={clientX:a,plotY:b,yBottom:c}},E=function(a,b,f){ca&&!G[a+","+b]&&(G[a+","+b]=!0,c.inverted&&(a=e.len-a,b=h.len-b),H.push({clientX:a,plotX:a,
|
||||
plotY:b,i:ea+f}))};(this.points||this.graph)&&this.destroyGraphics();a.plotGroup("group","series",a.visible?"visible":"hidden",b.zIndex,c.seriesGroup);a.markerGroup=a.group;W(a,"destroy",function(){a.markerGroup=null});H=this.points=[];k=this.getContext();a.buildKDTree=U;99999<q.length&&(c.options.loading=Y(m,{labelStyle:{backgroundColor:f.color("#ffffff").setOpacity(.75).get(),padding:"1em",borderRadius:"0.5em"},style:{backgroundColor:"none",opacity:1}}),clearTimeout(F),c.showLoading("Drawing..."),
|
||||
c.options.loading=m);n(P?a.data:l||q,function(b,f){var d,g,k,l,m="undefined"===typeof c.index,n=!0;if(!m){R?(d=b[0],g=b[1]):(d=b,g=p[f]);O?(R&&(g=b.slice(1,3)),l=g[0],g=g[1]):P&&(d=b.x,g=b.stackY,l=g-b.y);k=null===g;fa||(n=g>=z&&g<=aa);if(!k&&d>=v&&d<=x&&n)if(d=Math.round(e.toPixels(d,!0)),ba){if(void 0===t||d===A){O||(l=g);if(void 0===w||g>D)D=g,w=f;if(void 0===t||l<C)C=l,t=f}d!==A&&(void 0!==t&&(g=h.toPixels(D,!0),r=h.toPixels(C,!0),T(d,M?Math.min(g,N):g,M?Math.max(r,N):r),E(d,g,w),r!==g&&E(d,r,
|
||||
t)),t=w=void 0,A=d)}else g=Math.round(h.toPixels(g,!0)),T(d,g,r),E(d,g,f);Q=k&&!ga;0===f%5E4&&a.canvasToSVG()}return!m},function(){var b=c.loadingDiv,e=c.loadingShown;S();a.canvasToSVG();X(a,"renderedCanvas");e&&(y(b.style,{transition:"opacity 250ms",opacity:0}),c.loadingShown=!1,F=setTimeout(function(){b.parentNode&&b.parentNode.removeChild(b);c.loadingDiv=c.loadingSpan=null},250));a.directTouch=!1;a.options.stickyTracking=!0;delete a.buildKDTree;a.buildKDTree()},c.renderer.forExport?Number.MAX_VALUE:
|
||||
void 0)}});d.scatter.prototype.cvsMarkerCircle=function(a,b,c,e){a.moveTo(b,c);a.arc(b,c,e,0,2*Math.PI,!1)};d.scatter.prototype.cvsMarkerSquare=function(a,b,c,e){a.rect(b-e,c-e,2*e,2*e)};d.scatter.prototype.fill=!0;y(d.area.prototype,{cvsDrawPoint:function(a,b,c,e,d){d&&b!==d.clientX&&(a.moveTo(d.clientX,d.yBottom),a.lineTo(d.clientX,d.plotY),a.lineTo(b,c),a.lineTo(b,e))},fill:!0,fillOpacity:!0,sampling:!0});y(d.column.prototype,{cvsDrawPoint:function(a,b,c,e){a.rect(b-1,c,1,e-c)},fill:!0,sampling:!0});
|
||||
p.prototype.getPoint=function(a){var b=a;!a||a instanceof this.pointClass||(b=(new this.pointClass).init(this,this.options.data[a.i]),b.category=b.x,b.dist=a.dist,b.distX=a.distX,b.plotX=a.plotX,b.plotY=a.plotY);return b};l(p.prototype,"destroy",function(a){var b=this,c=b.chart;c.hoverPoints&&(c.hoverPoints=z(c.hoverPoints,function(a){return a.series===b}));c.hoverPoint&&c.hoverPoint.series===b&&(c.hoverPoint=null);a.call(this)});l(p.prototype,"searchPoint",function(a){return this.getPoint(a.apply(this,
|
||||
[].slice.call(arguments,1)))})})(n)});
|
636
high/highcharts/js/modules/boost.src.js
Normal file
636
high/highcharts/js/modules/boost.src.js
Normal file
@ -0,0 +1,636 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
* Boost module
|
||||
*
|
||||
* (c) 2010-2016 Highsoft AS
|
||||
* Author: Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* License: www.highcharts.com/license
|
||||
* Author: Torstein Honsi
|
||||
*
|
||||
* This is an experimental Highcharts module that draws long data series on a canvas
|
||||
* in order to increase performance of the initial load time and tooltip responsiveness.
|
||||
*
|
||||
* Compatible with HTML5 canvas compatible browsers (not IE < 9).
|
||||
*
|
||||
*
|
||||
*
|
||||
* Development plan
|
||||
* - Column range.
|
||||
* - Heatmap.
|
||||
* - Treemap.
|
||||
* - Check how it works with Highstock and data grouping. Currently it only works when navigator.adaptToUpdatedData
|
||||
* is false. It is also recommended to set scrollbar.liveRedraw to false.
|
||||
* - Check inverted charts.
|
||||
* - Check reversed axes.
|
||||
* - Chart callback should be async after last series is drawn. (But not necessarily, we don't do
|
||||
that with initial series animation).
|
||||
* - Cache full-size image so we don't have to redraw on hide/show and zoom up. But k-d-tree still
|
||||
* needs to be built.
|
||||
* - Test IE9 and IE10.
|
||||
* - Stacking is not perhaps not correct since it doesn't use the translation given in
|
||||
* the translate method. If this gets to complicated, a possible way out would be to
|
||||
* have a simplified renderCanvas method that simply draws the areaPath on a canvas.
|
||||
*
|
||||
* If this module is taken in as part of the core
|
||||
* - All the loading logic should be merged with core. Update styles in the core.
|
||||
* - Most of the method wraps should probably be added directly in parent methods.
|
||||
*
|
||||
* Notes for boost mode
|
||||
* - Area lines are not drawn
|
||||
* - Point markers are not drawn on line-type series
|
||||
* - Lines are not drawn on scatter charts
|
||||
* - Zones and negativeColor don't work
|
||||
* - Columns are always one pixel wide. Don't set the threshold too low.
|
||||
*
|
||||
* Optimizing tips for users
|
||||
* - For scatter plots, use a marker.radius of 1 or less. It results in a rectangle being drawn, which is
|
||||
* considerably faster than a circle.
|
||||
* - Set extremes (min, max) explicitly on the axes in order for Highcharts to avoid computing extremes.
|
||||
* - Set enableMouseTracking to false on the series to improve total rendering time.
|
||||
* - The default threshold is set based on one series. If you have multiple, dense series, the combined
|
||||
* number of points drawn gets higher, and you may want to set the threshold lower in order to
|
||||
* use optimizations.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var win = H.win,
|
||||
doc = win.document,
|
||||
noop = function() {},
|
||||
Color = H.Color,
|
||||
Series = H.Series,
|
||||
seriesTypes = H.seriesTypes,
|
||||
each = H.each,
|
||||
extend = H.extend,
|
||||
addEvent = H.addEvent,
|
||||
fireEvent = H.fireEvent,
|
||||
grep = H.grep,
|
||||
isNumber = H.isNumber,
|
||||
merge = H.merge,
|
||||
pick = H.pick,
|
||||
wrap = H.wrap,
|
||||
plotOptions = H.getOptions().plotOptions,
|
||||
CHUNK_SIZE = 50000,
|
||||
destroyLoadingDiv;
|
||||
|
||||
function eachAsync(arr, fn, finalFunc, chunkSize, i) {
|
||||
i = i || 0;
|
||||
chunkSize = chunkSize || CHUNK_SIZE;
|
||||
|
||||
var threshold = i + chunkSize,
|
||||
proceed = true;
|
||||
|
||||
while (proceed && i < threshold && i < arr.length) {
|
||||
proceed = fn(arr[i], i);
|
||||
i = i + 1;
|
||||
}
|
||||
if (proceed) {
|
||||
if (i < arr.length) {
|
||||
setTimeout(function() {
|
||||
eachAsync(arr, fn, finalFunc, chunkSize, i);
|
||||
});
|
||||
} else if (finalFunc) {
|
||||
finalFunc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set default options
|
||||
each(['area', 'arearange', 'column', 'line', 'scatter'], function(type) {
|
||||
if (plotOptions[type]) {
|
||||
plotOptions[type].boostThreshold = 5000;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Override a bunch of methods the same way. If the number of points is below the threshold,
|
||||
* run the original method. If not, check for a canvas version or do nothing.
|
||||
*/
|
||||
each(['translate', 'generatePoints', 'drawTracker', 'drawPoints', 'render'], function(method) {
|
||||
function branch(proceed) {
|
||||
var letItPass = this.options.stacking && (method === 'translate' || method === 'generatePoints');
|
||||
if ((this.processedXData || this.options.data).length < (this.options.boostThreshold || Number.MAX_VALUE) ||
|
||||
letItPass) {
|
||||
|
||||
// Clear image
|
||||
if (method === 'render' && this.image) {
|
||||
this.image.attr({
|
||||
href: ''
|
||||
});
|
||||
this.animate = null; // We're zooming in, don't run animation
|
||||
}
|
||||
|
||||
proceed.call(this);
|
||||
|
||||
// If a canvas version of the method exists, like renderCanvas(), run
|
||||
} else if (this[method + 'Canvas']) {
|
||||
|
||||
this[method + 'Canvas']();
|
||||
}
|
||||
}
|
||||
wrap(Series.prototype, method, branch);
|
||||
|
||||
// A special case for some types - its translate method is already wrapped
|
||||
if (method === 'translate') {
|
||||
if (seriesTypes.column) {
|
||||
wrap(seriesTypes.column.prototype, method, branch);
|
||||
}
|
||||
if (seriesTypes.arearange) {
|
||||
wrap(seriesTypes.arearange.prototype, method, branch);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Do not compute extremes when min and max are set.
|
||||
* If we use this in the core, we can add the hook to hasExtremes to the methods directly.
|
||||
*/
|
||||
wrap(Series.prototype, 'getExtremes', function(proceed) {
|
||||
if (!this.hasExtremes()) {
|
||||
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
}
|
||||
});
|
||||
wrap(Series.prototype, 'setData', function(proceed) {
|
||||
if (!this.hasExtremes(true)) {
|
||||
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
}
|
||||
});
|
||||
wrap(Series.prototype, 'processData', function(proceed) {
|
||||
if (!this.hasExtremes(true)) {
|
||||
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
H.extend(Series.prototype, {
|
||||
pointRange: 0,
|
||||
allowDG: false, // No data grouping, let boost handle large data
|
||||
hasExtremes: function(checkX) {
|
||||
var options = this.options,
|
||||
data = options.data,
|
||||
xAxis = this.xAxis && this.xAxis.options,
|
||||
yAxis = this.yAxis && this.yAxis.options;
|
||||
return data.length > (options.boostThreshold || Number.MAX_VALUE) && isNumber(yAxis.min) && isNumber(yAxis.max) &&
|
||||
(!checkX || (isNumber(xAxis.min) && isNumber(xAxis.max)));
|
||||
},
|
||||
|
||||
/**
|
||||
* If implemented in the core, parts of this can probably be shared with other similar
|
||||
* methods in Highcharts.
|
||||
*/
|
||||
destroyGraphics: function() {
|
||||
var series = this,
|
||||
points = this.points,
|
||||
point,
|
||||
i;
|
||||
|
||||
if (points) {
|
||||
for (i = 0; i < points.length; i = i + 1) {
|
||||
point = points[i];
|
||||
if (point && point.graphic) {
|
||||
point.graphic = point.graphic.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
each(['graph', 'area', 'tracker'], function(prop) {
|
||||
if (series[prop]) {
|
||||
series[prop] = series[prop].destroy();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a hidden canvas to draw the graph on. The contents is later copied over
|
||||
* to an SVG image element.
|
||||
*/
|
||||
getContext: function() {
|
||||
var chart = this.chart,
|
||||
width = chart.plotWidth,
|
||||
height = chart.plotHeight,
|
||||
ctx = this.ctx,
|
||||
swapXY = function(proceed, x, y, a, b, c, d) {
|
||||
proceed.call(this, y, x, a, b, c, d);
|
||||
};
|
||||
|
||||
if (!this.canvas) {
|
||||
this.canvas = doc.createElement('canvas');
|
||||
this.image = chart.renderer.image('', 0, 0, width, height).add(this.group);
|
||||
this.ctx = ctx = this.canvas.getContext('2d');
|
||||
if (chart.inverted) {
|
||||
each(['moveTo', 'lineTo', 'rect', 'arc'], function(fn) {
|
||||
wrap(ctx, fn, swapXY);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
this.canvas.width = width;
|
||||
this.canvas.height = height;
|
||||
this.image.attr({
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
|
||||
return ctx;
|
||||
},
|
||||
|
||||
/**
|
||||
* Draw the canvas image inside an SVG image
|
||||
*/
|
||||
canvasToSVG: function() {
|
||||
this.image.attr({
|
||||
href: this.canvas.toDataURL('image/png')
|
||||
});
|
||||
},
|
||||
|
||||
cvsLineTo: function(ctx, clientX, plotY) {
|
||||
ctx.lineTo(clientX, plotY);
|
||||
},
|
||||
|
||||
renderCanvas: function() {
|
||||
var series = this,
|
||||
options = series.options,
|
||||
chart = series.chart,
|
||||
xAxis = this.xAxis,
|
||||
yAxis = this.yAxis,
|
||||
ctx,
|
||||
c = 0,
|
||||
xData = series.processedXData,
|
||||
yData = series.processedYData,
|
||||
rawData = options.data,
|
||||
xExtremes = xAxis.getExtremes(),
|
||||
xMin = xExtremes.min,
|
||||
xMax = xExtremes.max,
|
||||
yExtremes = yAxis.getExtremes(),
|
||||
yMin = yExtremes.min,
|
||||
yMax = yExtremes.max,
|
||||
pointTaken = {},
|
||||
lastClientX,
|
||||
sampling = !!series.sampling,
|
||||
points,
|
||||
r = options.marker && options.marker.radius,
|
||||
cvsDrawPoint = this.cvsDrawPoint,
|
||||
cvsLineTo = options.lineWidth ? this.cvsLineTo : false,
|
||||
cvsMarker = r <= 1 ? this.cvsMarkerSquare : this.cvsMarkerCircle,
|
||||
enableMouseTracking = options.enableMouseTracking !== false,
|
||||
lastPoint,
|
||||
threshold = options.threshold,
|
||||
yBottom = yAxis.getThreshold(threshold),
|
||||
hasThreshold = isNumber(threshold),
|
||||
translatedThreshold = yBottom,
|
||||
doFill = this.fill,
|
||||
isRange = series.pointArrayMap && series.pointArrayMap.join(',') === 'low,high',
|
||||
isStacked = !!options.stacking,
|
||||
cropStart = series.cropStart || 0,
|
||||
loadingOptions = chart.options.loading,
|
||||
requireSorting = series.requireSorting,
|
||||
wasNull,
|
||||
connectNulls = options.connectNulls,
|
||||
useRaw = !xData,
|
||||
minVal,
|
||||
maxVal,
|
||||
minI,
|
||||
maxI,
|
||||
fillColor = series.fillOpacity ?
|
||||
new Color(series.color).setOpacity(pick(options.fillOpacity, 0.75)).get() :
|
||||
series.color,
|
||||
stroke = function() {
|
||||
if (doFill) {
|
||||
ctx.fillStyle = fillColor;
|
||||
ctx.fill();
|
||||
} else {
|
||||
ctx.strokeStyle = series.color;
|
||||
ctx.lineWidth = options.lineWidth;
|
||||
ctx.stroke();
|
||||
}
|
||||
},
|
||||
drawPoint = function(clientX, plotY, yBottom) {
|
||||
if (c === 0) {
|
||||
ctx.beginPath();
|
||||
|
||||
if (cvsLineTo) {
|
||||
ctx.lineJoin = 'round';
|
||||
}
|
||||
}
|
||||
|
||||
if (wasNull) {
|
||||
ctx.moveTo(clientX, plotY);
|
||||
} else {
|
||||
if (cvsDrawPoint) {
|
||||
cvsDrawPoint(ctx, clientX, plotY, yBottom, lastPoint);
|
||||
} else if (cvsLineTo) {
|
||||
cvsLineTo(ctx, clientX, plotY);
|
||||
} else if (cvsMarker) {
|
||||
cvsMarker(ctx, clientX, plotY, r);
|
||||
}
|
||||
}
|
||||
|
||||
// We need to stroke the line for every 1000 pixels. It will crash the browser
|
||||
// memory use if we stroke too infrequently.
|
||||
c = c + 1;
|
||||
if (c === 1000) {
|
||||
stroke();
|
||||
c = 0;
|
||||
}
|
||||
|
||||
// Area charts need to keep track of the last point
|
||||
lastPoint = {
|
||||
clientX: clientX,
|
||||
plotY: plotY,
|
||||
yBottom: yBottom
|
||||
};
|
||||
},
|
||||
|
||||
addKDPoint = function(clientX, plotY, i) {
|
||||
|
||||
// The k-d tree requires series points. Reduce the amount of points, since the time to build the
|
||||
// tree increases exponentially.
|
||||
if (enableMouseTracking && !pointTaken[clientX + ',' + plotY]) {
|
||||
pointTaken[clientX + ',' + plotY] = true;
|
||||
|
||||
if (chart.inverted) {
|
||||
clientX = xAxis.len - clientX;
|
||||
plotY = yAxis.len - plotY;
|
||||
}
|
||||
|
||||
points.push({
|
||||
clientX: clientX,
|
||||
plotX: clientX,
|
||||
plotY: plotY,
|
||||
i: cropStart + i
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// If we are zooming out from SVG mode, destroy the graphics
|
||||
if (this.points || this.graph) {
|
||||
this.destroyGraphics();
|
||||
}
|
||||
|
||||
// The group
|
||||
series.plotGroup(
|
||||
'group',
|
||||
'series',
|
||||
series.visible ? 'visible' : 'hidden',
|
||||
options.zIndex,
|
||||
chart.seriesGroup
|
||||
);
|
||||
|
||||
series.markerGroup = series.group;
|
||||
addEvent(series, 'destroy', function() {
|
||||
series.markerGroup = null;
|
||||
});
|
||||
|
||||
points = this.points = [];
|
||||
ctx = this.getContext();
|
||||
series.buildKDTree = noop; // Do not start building while drawing
|
||||
|
||||
// Display a loading indicator
|
||||
if (rawData.length > 99999) {
|
||||
chart.options.loading = merge(loadingOptions, {
|
||||
labelStyle: {
|
||||
backgroundColor: H.color('#ffffff').setOpacity(0.75).get(),
|
||||
padding: '1em',
|
||||
borderRadius: '0.5em'
|
||||
},
|
||||
style: {
|
||||
backgroundColor: 'none',
|
||||
opacity: 1
|
||||
}
|
||||
});
|
||||
clearTimeout(destroyLoadingDiv);
|
||||
chart.showLoading('Drawing...');
|
||||
chart.options.loading = loadingOptions; // reset
|
||||
}
|
||||
|
||||
// Loop over the points
|
||||
eachAsync(isStacked ? series.data : (xData || rawData), function(d, i) {
|
||||
var x,
|
||||
y,
|
||||
clientX,
|
||||
plotY,
|
||||
isNull,
|
||||
low,
|
||||
chartDestroyed = typeof chart.index === 'undefined',
|
||||
isYInside = true;
|
||||
|
||||
if (!chartDestroyed) {
|
||||
if (useRaw) {
|
||||
x = d[0];
|
||||
y = d[1];
|
||||
} else {
|
||||
x = d;
|
||||
y = yData[i];
|
||||
}
|
||||
|
||||
// Resolve low and high for range series
|
||||
if (isRange) {
|
||||
if (useRaw) {
|
||||
y = d.slice(1, 3);
|
||||
}
|
||||
low = y[0];
|
||||
y = y[1];
|
||||
} else if (isStacked) {
|
||||
x = d.x;
|
||||
y = d.stackY;
|
||||
low = y - d.y;
|
||||
}
|
||||
|
||||
isNull = y === null;
|
||||
|
||||
// Optimize for scatter zooming
|
||||
if (!requireSorting) {
|
||||
isYInside = y >= yMin && y <= yMax;
|
||||
}
|
||||
|
||||
if (!isNull && x >= xMin && x <= xMax && isYInside) {
|
||||
|
||||
clientX = Math.round(xAxis.toPixels(x, true));
|
||||
|
||||
if (sampling) {
|
||||
if (minI === undefined || clientX === lastClientX) {
|
||||
if (!isRange) {
|
||||
low = y;
|
||||
}
|
||||
if (maxI === undefined || y > maxVal) {
|
||||
maxVal = y;
|
||||
maxI = i;
|
||||
}
|
||||
if (minI === undefined || low < minVal) {
|
||||
minVal = low;
|
||||
minI = i;
|
||||
}
|
||||
|
||||
}
|
||||
if (clientX !== lastClientX) { // Add points and reset
|
||||
if (minI !== undefined) { // then maxI is also a number
|
||||
plotY = yAxis.toPixels(maxVal, true);
|
||||
yBottom = yAxis.toPixels(minVal, true);
|
||||
drawPoint(
|
||||
clientX,
|
||||
hasThreshold ? Math.min(plotY, translatedThreshold) : plotY,
|
||||
hasThreshold ? Math.max(yBottom, translatedThreshold) : yBottom
|
||||
);
|
||||
addKDPoint(clientX, plotY, maxI);
|
||||
if (yBottom !== plotY) {
|
||||
addKDPoint(clientX, yBottom, minI);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
minI = maxI = undefined;
|
||||
lastClientX = clientX;
|
||||
}
|
||||
} else {
|
||||
plotY = Math.round(yAxis.toPixels(y, true));
|
||||
drawPoint(clientX, plotY, yBottom);
|
||||
addKDPoint(clientX, plotY, i);
|
||||
}
|
||||
}
|
||||
wasNull = isNull && !connectNulls;
|
||||
|
||||
if (i % CHUNK_SIZE === 0) {
|
||||
series.canvasToSVG();
|
||||
}
|
||||
}
|
||||
|
||||
return !chartDestroyed;
|
||||
}, function() {
|
||||
var loadingDiv = chart.loadingDiv,
|
||||
loadingShown = chart.loadingShown;
|
||||
stroke();
|
||||
series.canvasToSVG();
|
||||
|
||||
fireEvent(series, 'renderedCanvas');
|
||||
|
||||
// Do not use chart.hideLoading, as it runs JS animation and will be blocked by buildKDTree.
|
||||
// CSS animation looks good, but then it must be deleted in timeout. If we add the module to core,
|
||||
// change hideLoading so we can skip this block.
|
||||
if (loadingShown) {
|
||||
extend(loadingDiv.style, {
|
||||
transition: 'opacity 250ms',
|
||||
opacity: 0
|
||||
});
|
||||
chart.loadingShown = false;
|
||||
destroyLoadingDiv = setTimeout(function() {
|
||||
if (loadingDiv.parentNode) { // In exporting it is falsy
|
||||
loadingDiv.parentNode.removeChild(loadingDiv);
|
||||
}
|
||||
chart.loadingDiv = chart.loadingSpan = null;
|
||||
}, 250);
|
||||
}
|
||||
|
||||
// Pass tests in Pointer.
|
||||
// Replace this with a single property, and replace when zooming in
|
||||
// below boostThreshold.
|
||||
series.directTouch = false;
|
||||
series.options.stickyTracking = true;
|
||||
|
||||
delete series.buildKDTree; // Go back to prototype, ready to build
|
||||
series.buildKDTree();
|
||||
|
||||
// Don't do async on export, the exportChart, getSVGForExport and getSVG methods are not chained for it.
|
||||
}, chart.renderer.forExport ? Number.MAX_VALUE : undefined);
|
||||
}
|
||||
});
|
||||
|
||||
seriesTypes.scatter.prototype.cvsMarkerCircle = function(ctx, clientX, plotY, r) {
|
||||
ctx.moveTo(clientX, plotY);
|
||||
ctx.arc(clientX, plotY, r, 0, 2 * Math.PI, false);
|
||||
};
|
||||
|
||||
// Rect is twice as fast as arc, should be used for small markers
|
||||
seriesTypes.scatter.prototype.cvsMarkerSquare = function(ctx, clientX, plotY, r) {
|
||||
ctx.rect(clientX - r, plotY - r, r * 2, r * 2);
|
||||
};
|
||||
seriesTypes.scatter.prototype.fill = true;
|
||||
|
||||
extend(seriesTypes.area.prototype, {
|
||||
cvsDrawPoint: function(ctx, clientX, plotY, yBottom, lastPoint) {
|
||||
if (lastPoint && clientX !== lastPoint.clientX) {
|
||||
ctx.moveTo(lastPoint.clientX, lastPoint.yBottom);
|
||||
ctx.lineTo(lastPoint.clientX, lastPoint.plotY);
|
||||
ctx.lineTo(clientX, plotY);
|
||||
ctx.lineTo(clientX, yBottom);
|
||||
}
|
||||
},
|
||||
fill: true,
|
||||
fillOpacity: true,
|
||||
sampling: true
|
||||
});
|
||||
|
||||
extend(seriesTypes.column.prototype, {
|
||||
cvsDrawPoint: function(ctx, clientX, plotY, yBottom) {
|
||||
ctx.rect(clientX - 1, plotY, 1, yBottom - plotY);
|
||||
},
|
||||
fill: true,
|
||||
sampling: true
|
||||
});
|
||||
|
||||
/**
|
||||
* Return a full Point object based on the index. The boost module uses stripped point objects
|
||||
* for performance reasons.
|
||||
* @param {Number} boostPoint A stripped-down point object
|
||||
* @returns {Object} A Point object as per http://api.highcharts.com/highcharts#Point
|
||||
*/
|
||||
Series.prototype.getPoint = function(boostPoint) {
|
||||
var point = boostPoint;
|
||||
|
||||
if (boostPoint && !(boostPoint instanceof this.pointClass)) {
|
||||
point = (new this.pointClass()).init(this, this.options.data[boostPoint.i]); // eslint-disable-line new-cap
|
||||
point.category = point.x;
|
||||
|
||||
point.dist = boostPoint.dist;
|
||||
point.distX = boostPoint.distX;
|
||||
point.plotX = boostPoint.plotX;
|
||||
point.plotY = boostPoint.plotY;
|
||||
}
|
||||
|
||||
return point;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extend series.destroy to also remove the fake k-d-tree points (#5137). Normally
|
||||
* this is handled by Series.destroy that calls Point.destroy, but the fake
|
||||
* search points are not registered like that.
|
||||
*/
|
||||
wrap(Series.prototype, 'destroy', function(proceed) {
|
||||
var series = this,
|
||||
chart = series.chart;
|
||||
if (chart.hoverPoints) {
|
||||
chart.hoverPoints = grep(chart.hoverPoints, function(point) {
|
||||
return point.series === series;
|
||||
});
|
||||
}
|
||||
|
||||
if (chart.hoverPoint && chart.hoverPoint.series === series) {
|
||||
chart.hoverPoint = null;
|
||||
}
|
||||
proceed.call(this);
|
||||
});
|
||||
|
||||
/**
|
||||
* Return a point instance from the k-d-tree
|
||||
*/
|
||||
wrap(Series.prototype, 'searchPoint', function(proceed) {
|
||||
return this.getPoint(
|
||||
proceed.apply(this, [].slice.call(arguments, 1))
|
||||
);
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
14
high/highcharts/js/modules/broken-axis.js
Normal file
14
high/highcharts/js/modules/broken-axis.js
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
|
||||
(c) 2009-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(h){"object"===typeof module&&module.exports?module.exports=h:h(Highcharts)})(function(h){(function(d){function h(){return Array.prototype.slice.call(arguments,1)}function v(e){e.apply(this);this.drawBreaks(this.xAxis,["x"]);this.drawBreaks(this.yAxis,t(this.pointArrayMap,["y"]))}var t=d.pick,q=d.wrap,u=d.each,y=d.extend,w=d.fireEvent,r=d.Axis,z=d.Series;y(r.prototype,{isInBreak:function(e,g){var b=e.repeat||Infinity,c=e.from,a=e.to-e.from,b=g>=c?(g-c)%b:b-(c-g)%b;return e.inclusive?b<=a:
|
||||
b<a&&0!==b},isInAnyBreak:function(e,g){var b=this.options.breaks,c=b&&b.length,a,f,n;if(c){for(;c--;)this.isInBreak(b[c],e)&&(a=!0,f||(f=t(b[c].showPoints,this.isXAxis?!1:!0)));n=a&&g?a&&!f:a}return n}});q(r.prototype,"setTickPositions",function(e){e.apply(this,Array.prototype.slice.call(arguments,1));if(this.options.breaks){var g=this.tickPositions,b=this.tickPositions.info,c=[],a;for(a=0;a<g.length;a++)this.isInAnyBreak(g[a])||c.push(g[a]);this.tickPositions=c;this.tickPositions.info=b}});q(r.prototype,
|
||||
"init",function(e,g,b){b.breaks&&b.breaks.length&&(b.ordinal=!1);e.call(this,g,b);if(this.options.breaks){var c=this;c.isBroken=!0;this.val2lin=function(a){var f=a,n,b;for(b=0;b<c.breakArray.length;b++)if(n=c.breakArray[b],n.to<=a)f-=n.len;else if(n.from>=a)break;else if(c.isInBreak(n,a)){f-=a-n.from;break}return f};this.lin2val=function(a){var f,b;for(b=0;b<c.breakArray.length&&!(f=c.breakArray[b],f.from>=a);b++)f.to<a?a+=f.len:c.isInBreak(f,a)&&(a+=f.len);return a};this.setExtremes=function(a,c,
|
||||
b,e,g){for(;this.isInAnyBreak(a);)a-=this.closestPointRange;for(;this.isInAnyBreak(c);)c-=this.closestPointRange;r.prototype.setExtremes.call(this,a,c,b,e,g)};this.setAxisTranslation=function(a){r.prototype.setAxisTranslation.call(this,a);var b=c.options.breaks;a=[];var e=[],g=0,m,k,p=c.userMin||c.min,d=c.userMax||c.max,l,h;for(h in b)k=b[h],m=k.repeat||Infinity,c.isInBreak(k,p)&&(p+=k.to%m-p%m),c.isInBreak(k,d)&&(d-=d%m-k.from%m);for(h in b){k=b[h];l=k.from;for(m=k.repeat||Infinity;l-m>p;)l-=m;for(;l<
|
||||
p;)l+=m;for(;l<d;l+=m)a.push({value:l,move:"in"}),a.push({value:l+(k.to-k.from),move:"out",size:k.breakSize})}a.sort(function(b,a){return b.value===a.value?("in"===b.move?0:1)-("in"===a.move?0:1):b.value-a.value});b=0;l=p;for(h in a)k=a[h],b+="in"===k.move?1:-1,1===b&&"in"===k.move&&(l=k.value),0===b&&(e.push({from:l,to:k.value,len:k.value-l-(k.size||0)}),g+=k.value-l-(k.size||0));c.breakArray=e;w(c,"afterBreaks");c.transA*=(d-c.min)/(d-p-g);c.min=p;c.max=d}}});q(z.prototype,"generatePoints",function(e){e.apply(this,
|
||||
h(arguments));var g=this.xAxis,b=this.yAxis,c=this.points,a,f=c.length,d=this.options.connectNulls,x;if(g&&b&&(g.options.breaks||b.options.breaks))for(;f--;)a=c[f],x=null===a.y&&!1===d,x||!g.isInAnyBreak(a.x,!0)&&!b.isInAnyBreak(a.y,!0)||(c.splice(f,1),this.data[f]&&this.data[f].destroyElements())});d.Series.prototype.drawBreaks=function(e,g){var b=this,c=b.points,a,f,d,h;u(g,function(g){a=e.breakArray||[];f=e.isXAxis?e.min:t(b.options.threshold,e.min);u(c,function(b){h=t(b["stack"+g.toUpperCase()],
|
||||
b[g]);u(a,function(a){d=!1;if(f<a.from&&h>a.to||f>a.from&&h<a.from)d="pointBreak";else if(f<a.from&&h>a.from&&h<a.to||f>a.from&&h>a.to&&h<a.from)d="pointInBreak";d&&w(e,d,{point:b,brk:a})})})})};q(d.seriesTypes.column.prototype,"drawPoints",v);q(d.Series.prototype,"drawPoints",v)})(h)});
|
334
high/highcharts/js/modules/broken-axis.src.js
Normal file
334
high/highcharts/js/modules/broken-axis.src.js
Normal file
@ -0,0 +1,334 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
*
|
||||
* (c) 2009-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* (c) 2009-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var pick = H.pick,
|
||||
wrap = H.wrap,
|
||||
each = H.each,
|
||||
extend = H.extend,
|
||||
fireEvent = H.fireEvent,
|
||||
Axis = H.Axis,
|
||||
Series = H.Series;
|
||||
|
||||
function stripArguments() {
|
||||
return Array.prototype.slice.call(arguments, 1);
|
||||
}
|
||||
|
||||
extend(Axis.prototype, {
|
||||
isInBreak: function(brk, val) {
|
||||
var ret,
|
||||
repeat = brk.repeat || Infinity,
|
||||
from = brk.from,
|
||||
length = brk.to - brk.from,
|
||||
test = (val >= from ? (val - from) % repeat : repeat - ((from - val) % repeat));
|
||||
|
||||
if (!brk.inclusive) {
|
||||
ret = test < length && test !== 0;
|
||||
} else {
|
||||
ret = test <= length;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
isInAnyBreak: function(val, testKeep) {
|
||||
|
||||
var breaks = this.options.breaks,
|
||||
i = breaks && breaks.length,
|
||||
inbrk,
|
||||
keep,
|
||||
ret;
|
||||
|
||||
|
||||
if (i) {
|
||||
|
||||
while (i--) {
|
||||
if (this.isInBreak(breaks[i], val)) {
|
||||
inbrk = true;
|
||||
if (!keep) {
|
||||
keep = pick(breaks[i].showPoints, this.isXAxis ? false : true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inbrk && testKeep) {
|
||||
ret = inbrk && !keep;
|
||||
} else {
|
||||
ret = inbrk;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
});
|
||||
|
||||
wrap(Axis.prototype, 'setTickPositions', function(proceed) {
|
||||
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
|
||||
if (this.options.breaks) {
|
||||
var axis = this,
|
||||
tickPositions = this.tickPositions,
|
||||
info = this.tickPositions.info,
|
||||
newPositions = [],
|
||||
i;
|
||||
|
||||
for (i = 0; i < tickPositions.length; i++) {
|
||||
if (!axis.isInAnyBreak(tickPositions[i])) {
|
||||
newPositions.push(tickPositions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this.tickPositions = newPositions;
|
||||
this.tickPositions.info = info;
|
||||
}
|
||||
});
|
||||
|
||||
wrap(Axis.prototype, 'init', function(proceed, chart, userOptions) {
|
||||
// Force Axis to be not-ordinal when breaks are defined
|
||||
if (userOptions.breaks && userOptions.breaks.length) {
|
||||
userOptions.ordinal = false;
|
||||
}
|
||||
|
||||
proceed.call(this, chart, userOptions);
|
||||
|
||||
if (this.options.breaks) {
|
||||
|
||||
var axis = this;
|
||||
|
||||
axis.isBroken = true;
|
||||
|
||||
this.val2lin = function(val) {
|
||||
var nval = val,
|
||||
brk,
|
||||
i;
|
||||
|
||||
for (i = 0; i < axis.breakArray.length; i++) {
|
||||
brk = axis.breakArray[i];
|
||||
if (brk.to <= val) {
|
||||
nval -= brk.len;
|
||||
} else if (brk.from >= val) {
|
||||
break;
|
||||
} else if (axis.isInBreak(brk, val)) {
|
||||
nval -= (val - brk.from);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nval;
|
||||
};
|
||||
|
||||
this.lin2val = function(val) {
|
||||
var nval = val,
|
||||
brk,
|
||||
i;
|
||||
|
||||
for (i = 0; i < axis.breakArray.length; i++) {
|
||||
brk = axis.breakArray[i];
|
||||
if (brk.from >= nval) {
|
||||
break;
|
||||
} else if (brk.to < nval) {
|
||||
nval += brk.len;
|
||||
} else if (axis.isInBreak(brk, nval)) {
|
||||
nval += brk.len;
|
||||
}
|
||||
}
|
||||
return nval;
|
||||
};
|
||||
|
||||
this.setExtremes = function(newMin, newMax, redraw, animation, eventArguments) {
|
||||
// If trying to set extremes inside a break, extend it to before and after the break ( #3857 )
|
||||
while (this.isInAnyBreak(newMin)) {
|
||||
newMin -= this.closestPointRange;
|
||||
}
|
||||
while (this.isInAnyBreak(newMax)) {
|
||||
newMax -= this.closestPointRange;
|
||||
}
|
||||
Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
|
||||
};
|
||||
|
||||
this.setAxisTranslation = function(saveOld) {
|
||||
Axis.prototype.setAxisTranslation.call(this, saveOld);
|
||||
|
||||
var breaks = axis.options.breaks,
|
||||
breakArrayT = [], // Temporary one
|
||||
breakArray = [],
|
||||
length = 0,
|
||||
inBrk,
|
||||
repeat,
|
||||
brk,
|
||||
min = axis.userMin || axis.min,
|
||||
max = axis.userMax || axis.max,
|
||||
start,
|
||||
i,
|
||||
j;
|
||||
|
||||
// Min & max check (#4247)
|
||||
for (i in breaks) {
|
||||
brk = breaks[i];
|
||||
repeat = brk.repeat || Infinity;
|
||||
if (axis.isInBreak(brk, min)) {
|
||||
min += (brk.to % repeat) - (min % repeat);
|
||||
}
|
||||
if (axis.isInBreak(brk, max)) {
|
||||
max -= (max % repeat) - (brk.from % repeat);
|
||||
}
|
||||
}
|
||||
|
||||
// Construct an array holding all breaks in the axis
|
||||
for (i in breaks) {
|
||||
brk = breaks[i];
|
||||
start = brk.from;
|
||||
repeat = brk.repeat || Infinity;
|
||||
|
||||
while (start - repeat > min) {
|
||||
start -= repeat;
|
||||
}
|
||||
while (start < min) {
|
||||
start += repeat;
|
||||
}
|
||||
|
||||
for (j = start; j < max; j += repeat) {
|
||||
breakArrayT.push({
|
||||
value: j,
|
||||
move: 'in'
|
||||
});
|
||||
breakArrayT.push({
|
||||
value: j + (brk.to - brk.from),
|
||||
move: 'out',
|
||||
size: brk.breakSize
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
breakArrayT.sort(function(a, b) {
|
||||
var ret;
|
||||
if (a.value === b.value) {
|
||||
ret = (a.move === 'in' ? 0 : 1) - (b.move === 'in' ? 0 : 1);
|
||||
} else {
|
||||
ret = a.value - b.value;
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
|
||||
// Simplify the breaks
|
||||
inBrk = 0;
|
||||
start = min;
|
||||
|
||||
for (i in breakArrayT) {
|
||||
brk = breakArrayT[i];
|
||||
inBrk += (brk.move === 'in' ? 1 : -1);
|
||||
|
||||
if (inBrk === 1 && brk.move === 'in') {
|
||||
start = brk.value;
|
||||
}
|
||||
if (inBrk === 0) {
|
||||
breakArray.push({
|
||||
from: start,
|
||||
to: brk.value,
|
||||
len: brk.value - start - (brk.size || 0)
|
||||
});
|
||||
length += brk.value - start - (brk.size || 0);
|
||||
}
|
||||
}
|
||||
|
||||
axis.breakArray = breakArray;
|
||||
|
||||
fireEvent(axis, 'afterBreaks');
|
||||
|
||||
axis.transA *= ((max - axis.min) / (max - min - length));
|
||||
|
||||
axis.min = min;
|
||||
axis.max = max;
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
wrap(Series.prototype, 'generatePoints', function(proceed) {
|
||||
|
||||
proceed.apply(this, stripArguments(arguments));
|
||||
|
||||
var series = this,
|
||||
xAxis = series.xAxis,
|
||||
yAxis = series.yAxis,
|
||||
points = series.points,
|
||||
point,
|
||||
i = points.length,
|
||||
connectNulls = series.options.connectNulls,
|
||||
nullGap;
|
||||
|
||||
|
||||
if (xAxis && yAxis && (xAxis.options.breaks || yAxis.options.breaks)) {
|
||||
while (i--) {
|
||||
point = points[i];
|
||||
|
||||
nullGap = point.y === null && connectNulls === false; // respect nulls inside the break (#4275)
|
||||
if (!nullGap && (xAxis.isInAnyBreak(point.x, true) || yAxis.isInAnyBreak(point.y, true))) {
|
||||
points.splice(i, 1);
|
||||
if (this.data[i]) {
|
||||
this.data[i].destroyElements(); // removes the graphics for this point if they exist
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function drawPointsWrapped(proceed) {
|
||||
proceed.apply(this);
|
||||
this.drawBreaks(this.xAxis, ['x']);
|
||||
this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
|
||||
}
|
||||
|
||||
H.Series.prototype.drawBreaks = function(axis, keys) {
|
||||
var series = this,
|
||||
points = series.points,
|
||||
breaks,
|
||||
threshold,
|
||||
eventName,
|
||||
y;
|
||||
|
||||
each(keys, function(key) {
|
||||
breaks = axis.breakArray || [];
|
||||
threshold = axis.isXAxis ? axis.min : pick(series.options.threshold, axis.min);
|
||||
each(points, function(point) {
|
||||
y = pick(point['stack' + key.toUpperCase()], point[key]);
|
||||
each(breaks, function(brk) {
|
||||
eventName = false;
|
||||
|
||||
if ((threshold < brk.from && y > brk.to) || (threshold > brk.from && y < brk.from)) {
|
||||
eventName = 'pointBreak';
|
||||
} else if ((threshold < brk.from && y > brk.from && y < brk.to) || (threshold > brk.from && y > brk.to && y < brk.from)) { // point falls inside the break
|
||||
eventName = 'pointInBreak';
|
||||
}
|
||||
if (eventName) {
|
||||
fireEvent(axis, eventName, {
|
||||
point: point,
|
||||
brk: brk
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
wrap(H.seriesTypes.column.prototype, 'drawPoints', drawPointsWrapped);
|
||||
wrap(H.Series.prototype, 'drawPoints', drawPointsWrapped);
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
26
high/highcharts/js/modules/data.js
Normal file
26
high/highcharts/js/modules/data.js
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
Data module
|
||||
|
||||
(c) 2012-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(q){"object"===typeof module&&module.exports?module.exports=q:q(Highcharts)})(function(q){(function(g){var q=g.win.document,m=g.each,A=g.pick,x=g.inArray,y=g.isNumber,B=g.splat,n,w=function(b,a){this.init(b,a)};g.extend(w.prototype,{init:function(b,a){this.options=b;this.chartOptions=a;this.columns=b.columns||this.rowsToColumns(b.rows)||[];this.firstRowAsNames=A(b.firstRowAsNames,!0);this.decimalRegex=b.decimalPoint&&new RegExp("^(-?[0-9]+)"+b.decimalPoint+"([0-9]+)$");this.rawColumns=[];
|
||||
this.columns.length?this.dataFound():(this.parseCSV(),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=[],v=0,k;m(b&&b.series||[],function(b){c.push(f(b.type||d))});m(a&&a.seriesMapping||[],function(b){e.push(b.x||0)});0===e.length&&e.push(0);m(a&&a.seriesMapping||[],function(a){var e=new n,t,r=c[v]||f(d),
|
||||
p=g.seriesTypes[((b&&b.series||[])[v]||{}).type||d||"line"].prototype.pointArrayMap||["y"];e.addColumnReader(a.x,"x");for(t in a)a.hasOwnProperty(t)&&"x"!==t&&e.addColumnReader(a[t],t);for(k=0;k<r;k++)e.hasReader(p[k])||e.addColumnReader(void 0,p[k]);h.push(e);v++});a=g.seriesTypes[d||"line"].prototype.pointArrayMap;void 0===a&&(a=["y"]);this.valueCount={global:f(d),xColumns:e,individual:c,seriesBuilders:h,globalPointArrayMap:a}},dataFound:function(){this.options.switchRowsAndColumns&&(this.columns=
|
||||
this.rowsToColumns(this.columns));this.getColumnDistribution();this.parseTypes();!1!==this.parsed()&&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,v=a.endColumn||Number.MAX_VALUE,k,g,z=0;e&&(g=e.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split(a.lineDelimiter||"\n"),k=a.itemDelimiter||(-1!==e.indexOf("\t")?"\t":","),m(g,function(a,e){var g=b.trim(a),u=0===g.indexOf("#");e>=d&&e<=c&&!u&&""!==g&&(g=
|
||||
a.split(k),m(g,function(b,a){a>=h&&a<=v&&(f[a-h]||(f[a-h]=[]),f[a-h][z]=b)}),z+=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&&("string"===typeof a&&(a=q.getElementById(a)),m(a.getElementsByTagName("tr"),function(b,a){a>=f&&a<=d&&m(b.children,function(b,d){("TD"===b.tagName||"TH"===b.tagName)&&d>=c&&d<=h&&(e[d-c]||(e[d-c]=[]),e[d-c][a-f]=b.innerHTML)})}),this.dataFound())},
|
||||
parseGoogleSpreadsheet:function(){var b=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,k,u;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){a=a.feed.entry;var e,r=a.length,p=0,n=0,l;for(l=0;l<r;l++)e=a[l],p=Math.max(p,e.gs$cell.col),
|
||||
n=Math.max(n,e.gs$cell.row);for(l=0;l<p;l++)l>=h&&l<=g&&(f[l-h]=[],f[l-h].length=Math.min(n,c-d));for(l=0;l<r;l++)e=a[l],k=e.gs$cell.row-1,u=e.gs$cell.col-1,u>=h&&u<=g&&k>=d&&k<=c&&(f[u-h][k-d]=e.content.$t);m(f,function(a){for(l=0;l<a.length;l++)void 0===a[l]&&(a[l]=null)});b.dataFound()}})},trim:function(b,a){"string"===typeof b&&(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,f=this.columns,d=b.length,c,h,g,k,n=this.firstRowAsNames,m=-1!==x(a,this.valueCount.xColumns),t=[],r=this.chartOptions,p,q=(this.options.columnTypes||[])[a],r=m&&(r&&r.xAxis&&"category"===B(r.xAxis)[0].type||"string"===q);for(e[a]||(e[a]=[]);d--;)c=t[d]||b[d],g=this.trim(c),k=this.trim(c,!0),h=parseFloat(k),void 0===e[a][d]&&(e[a][d]=g),r||0===d&&n?b[d]=g:+k===h?(b[d]=h,31536E6<h&&"float"!==q?b.isDatetime=
|
||||
!0:b.isNumeric=!0,void 0!==b[d+1]&&(p=h>b[d+1])):(h=this.parseDate(c),m&&y(h)&&"float"!==q?(t[d]=c,b[d]=h,b.isDatetime=!0,void 0!==b[d+1]&&(c=h>b[d+1],c!==p&&void 0!==p&&(this.alternativeFormat?(this.dateFormat=this.alternativeFormat,d=b.length,this.alternativeFormat=this.dateFormats[this.dateFormat].alternative):b.unsorted=!0),p=c)):(b[d]=""===g?null:g,0!==d&&(b.isDatetime||b.isNumeric)&&(b.mixed=!0)));m&&b.mixed&&(f[a]=e[a]);if(m&&p&&this.options.sort)for(a=0;a<f.length;a++)f[a].reverse(),n&&f[a].unshift(f[a].pop())},
|
||||
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])}},"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"},"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("string"===typeof b){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),"object"===typeof c&&null!==c&&c.getTime?e=c.getTime()-6E4*c.getTimezoneOffset():y(c)&&(e=c-6E4*(new Date(c)).getTimezoneOffset()))}return e},rowsToColumns:function(b){var a,e,f,d,c;if(b)for(c=[],e=b.length,a=0;a<e;a++)for(d=b[a].length,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+=1)for(h=a[e].getReferencedColumnIndexes(),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=[],k;if(e.complete||e.afterComplete){for(c=0;c<b.length;c++)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++)k=this.valueCount.seriesBuilders[c],
|
||||
k.populateColumns(d)&&g.push(k);for(;0<d.length;){k=new n;k.addColumnReader(0,"x");c=x(0,d);-1!==c&&d.splice(c,1);for(c=0;c<this.valueCount.global;c++)k.addColumnReader(void 0,this.valueCount.globalPointArrayMap[c]);k.populateColumns(d)&&g.push(k)}0<g.length&&0<g[0].readers.length&&(k=b[g[0].readers[0].columnIndex],void 0!==k&&(k.isDatetime?a="datetime":k.isNumeric||(a="category")));if("category"===a)for(c=0;c<g.length;c++)for(k=g[c],d=0;d<k.readers.length;d++)"x"===k.readers[d].configName&&(k.readers[d].configName=
|
||||
"name");for(c=0;c<g.length;c++){k=g[c];d=[];for(h=0;h<b[0].length;h++)d[h]=k.read(b,h);f[c]={data:d};k.name&&(f[c].name=k.name);"category"===a&&(f[c].turboThreshold=0)}b={series:f};a&&(b.xAxis={type:a},"category"===a&&(b.xAxis.nameToX=!1));e.complete&&e.complete(b);e.afterComplete&&e.afterComplete(b)}}});g.Data=w;g.data=function(b,a){return new w(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("object"===
|
||||
typeof a.series)for(c=Math.max(a.series.length,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)});n=function(){this.readers=[];this.pointIsArray=!0};n.prototype.populateColumns=function(b){var a=!0;m(this.readers,function(a){void 0===a.columnIndex&&(a.columnIndex=b.shift())});m(this.readers,function(b){void 0===b.columnIndex&&(a=!1)});return a};n.prototype.read=function(b,a){var e=this.pointIsArray,f=e?
|
||||
[]:{},d;m(this.readers,function(c){var d=b[c.columnIndex][a];e?f.push(d):f[c.configName]=d});void 0===this.name&&2<=this.readers.length&&(d=this.getReferencedColumnIndexes(),2<=d.length&&(d.shift(),d.sort(),this.name=b[d.shift()].name));return f};n.prototype.addColumnReader=function(b,a){this.readers.push({columnIndex:b,configName:a});"x"!==a&&"y"!==a&&void 0!==a&&(this.pointIsArray=!1)};n.prototype.getReferencedColumnIndexes=function(){var b,a=[],e;for(b=0;b<this.readers.length;b+=1)e=this.readers[b],
|
||||
void 0!==e.columnIndex&&a.push(e.columnIndex);return a};n.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}})(q)});
|
981
high/highcharts/js/modules/data.src.js
Normal file
981
high/highcharts/js/modules/data.src.js
Normal file
@ -0,0 +1,981 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
* Data module
|
||||
*
|
||||
* (c) 2012-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(Highcharts) {
|
||||
/**
|
||||
* Data module
|
||||
*
|
||||
* (c) 2012-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
|
||||
/* global jQuery */
|
||||
'use strict';
|
||||
|
||||
// Utilities
|
||||
var win = Highcharts.win,
|
||||
doc = win.document,
|
||||
each = Highcharts.each,
|
||||
pick = Highcharts.pick,
|
||||
inArray = Highcharts.inArray,
|
||||
isNumber = Highcharts.isNumber,
|
||||
splat = Highcharts.splat,
|
||||
SeriesBuilder;
|
||||
|
||||
|
||||
// The Data constructor
|
||||
var Data = function(dataOptions, chartOptions) {
|
||||
this.init(dataOptions, chartOptions);
|
||||
};
|
||||
|
||||
// Set the prototype properties
|
||||
Highcharts.extend(Data.prototype, {
|
||||
|
||||
/**
|
||||
* Initialize the Data object with the given options
|
||||
*/
|
||||
init: function(options, chartOptions) {
|
||||
this.options = options;
|
||||
this.chartOptions = chartOptions;
|
||||
this.columns = options.columns || this.rowsToColumns(options.rows) || [];
|
||||
this.firstRowAsNames = pick(options.firstRowAsNames, true);
|
||||
this.decimalRegex = options.decimalPoint && new RegExp('^(-?[0-9]+)' + options.decimalPoint + '([0-9]+)$');
|
||||
|
||||
// This is a two-dimensional array holding the raw, trimmed string values
|
||||
// with the same organisation as the columns array. It makes it possible
|
||||
// for example to revert from interpreted timestamps to string-based
|
||||
// categories.
|
||||
this.rawColumns = [];
|
||||
|
||||
// No need to parse or interpret anything
|
||||
if (this.columns.length) {
|
||||
this.dataFound();
|
||||
|
||||
// Parse and interpret
|
||||
} else {
|
||||
|
||||
// Parse a CSV string if options.csv is given
|
||||
this.parseCSV();
|
||||
|
||||
// Parse a HTML table if options.table is given
|
||||
this.parseTable();
|
||||
|
||||
// Parse a Google Spreadsheet
|
||||
this.parseGoogleSpreadsheet();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the column distribution. For example, a line series takes a single column for
|
||||
* Y values. A range series takes two columns for low and high values respectively,
|
||||
* and an OHLC series takes four columns.
|
||||
*/
|
||||
getColumnDistribution: function() {
|
||||
var chartOptions = this.chartOptions,
|
||||
options = this.options,
|
||||
xColumns = [],
|
||||
getValueCount = function(type) {
|
||||
return (Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap || [0]).length;
|
||||
},
|
||||
getPointArrayMap = function(type) {
|
||||
return Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap;
|
||||
},
|
||||
globalType = chartOptions && chartOptions.chart && chartOptions.chart.type,
|
||||
individualCounts = [],
|
||||
seriesBuilders = [],
|
||||
seriesIndex = 0,
|
||||
i;
|
||||
|
||||
each((chartOptions && chartOptions.series) || [], function(series) {
|
||||
individualCounts.push(getValueCount(series.type || globalType));
|
||||
});
|
||||
|
||||
// Collect the x-column indexes from seriesMapping
|
||||
each((options && options.seriesMapping) || [], function(mapping) {
|
||||
xColumns.push(mapping.x || 0);
|
||||
});
|
||||
|
||||
// If there are no defined series with x-columns, use the first column as x column
|
||||
if (xColumns.length === 0) {
|
||||
xColumns.push(0);
|
||||
}
|
||||
|
||||
// Loop all seriesMappings and constructs SeriesBuilders from
|
||||
// the mapping options.
|
||||
each((options && options.seriesMapping) || [], function(mapping) {
|
||||
var builder = new SeriesBuilder(),
|
||||
name,
|
||||
numberOfValueColumnsNeeded = individualCounts[seriesIndex] || getValueCount(globalType),
|
||||
seriesArr = (chartOptions && chartOptions.series) || [],
|
||||
series = seriesArr[seriesIndex] || {},
|
||||
pointArrayMap = getPointArrayMap(series.type || globalType) || ['y'];
|
||||
|
||||
// Add an x reader from the x property or from an undefined column
|
||||
// if the property is not set. It will then be auto populated later.
|
||||
builder.addColumnReader(mapping.x, 'x');
|
||||
|
||||
// Add all column mappings
|
||||
for (name in mapping) {
|
||||
if (mapping.hasOwnProperty(name) && name !== 'x') {
|
||||
builder.addColumnReader(mapping[name], name);
|
||||
}
|
||||
}
|
||||
|
||||
// Add missing columns
|
||||
for (i = 0; i < numberOfValueColumnsNeeded; i++) {
|
||||
if (!builder.hasReader(pointArrayMap[i])) {
|
||||
//builder.addNextColumnReader(pointArrayMap[i]);
|
||||
// Create and add a column reader for the next free column index
|
||||
builder.addColumnReader(undefined, pointArrayMap[i]);
|
||||
}
|
||||
}
|
||||
|
||||
seriesBuilders.push(builder);
|
||||
seriesIndex++;
|
||||
});
|
||||
|
||||
var globalPointArrayMap = getPointArrayMap(globalType);
|
||||
if (globalPointArrayMap === undefined) {
|
||||
globalPointArrayMap = ['y'];
|
||||
}
|
||||
|
||||
this.valueCount = {
|
||||
global: getValueCount(globalType),
|
||||
xColumns: xColumns,
|
||||
individual: individualCounts,
|
||||
seriesBuilders: seriesBuilders,
|
||||
globalPointArrayMap: globalPointArrayMap
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* When the data is parsed into columns, either by CSV, table, GS or direct input,
|
||||
* continue with other operations.
|
||||
*/
|
||||
dataFound: function() {
|
||||
|
||||
if (this.options.switchRowsAndColumns) {
|
||||
this.columns = this.rowsToColumns(this.columns);
|
||||
}
|
||||
|
||||
// Interpret the info about series and columns
|
||||
this.getColumnDistribution();
|
||||
|
||||
// Interpret the values into right types
|
||||
this.parseTypes();
|
||||
|
||||
// Handle columns if a handleColumns callback is given
|
||||
if (this.parsed() !== false) {
|
||||
|
||||
// Complete if a complete callback is given
|
||||
this.complete();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a CSV input string
|
||||
*/
|
||||
parseCSV: function() {
|
||||
var self = this,
|
||||
options = this.options,
|
||||
csv = options.csv,
|
||||
columns = this.columns,
|
||||
startRow = options.startRow || 0,
|
||||
endRow = options.endRow || Number.MAX_VALUE,
|
||||
startColumn = options.startColumn || 0,
|
||||
endColumn = options.endColumn || Number.MAX_VALUE,
|
||||
itemDelimiter,
|
||||
lines,
|
||||
activeRowNo = 0;
|
||||
|
||||
if (csv) {
|
||||
|
||||
lines = csv
|
||||
.replace(/\r\n/g, '\n') // Unix
|
||||
.replace(/\r/g, '\n') // Mac
|
||||
.split(options.lineDelimiter || '\n');
|
||||
|
||||
itemDelimiter = options.itemDelimiter || (csv.indexOf('\t') !== -1 ? '\t' : ',');
|
||||
|
||||
each(lines, function(line, rowNo) {
|
||||
var trimmed = self.trim(line),
|
||||
isComment = trimmed.indexOf('#') === 0,
|
||||
isBlank = trimmed === '',
|
||||
items;
|
||||
|
||||
if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) {
|
||||
items = line.split(itemDelimiter);
|
||||
each(items, function(item, colNo) {
|
||||
if (colNo >= startColumn && colNo <= endColumn) {
|
||||
if (!columns[colNo - startColumn]) {
|
||||
columns[colNo - startColumn] = [];
|
||||
}
|
||||
|
||||
columns[colNo - startColumn][activeRowNo] = item;
|
||||
}
|
||||
});
|
||||
activeRowNo += 1;
|
||||
}
|
||||
});
|
||||
|
||||
this.dataFound();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a HTML table
|
||||
*/
|
||||
parseTable: function() {
|
||||
var options = this.options,
|
||||
table = options.table,
|
||||
columns = this.columns,
|
||||
startRow = options.startRow || 0,
|
||||
endRow = options.endRow || Number.MAX_VALUE,
|
||||
startColumn = options.startColumn || 0,
|
||||
endColumn = options.endColumn || Number.MAX_VALUE;
|
||||
|
||||
if (table) {
|
||||
|
||||
if (typeof table === 'string') {
|
||||
table = doc.getElementById(table);
|
||||
}
|
||||
|
||||
each(table.getElementsByTagName('tr'), function(tr, rowNo) {
|
||||
if (rowNo >= startRow && rowNo <= endRow) {
|
||||
each(tr.children, function(item, colNo) {
|
||||
if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) {
|
||||
if (!columns[colNo - startColumn]) {
|
||||
columns[colNo - startColumn] = [];
|
||||
}
|
||||
|
||||
columns[colNo - startColumn][rowNo - startRow] = item.innerHTML;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.dataFound(); // continue
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*/
|
||||
parseGoogleSpreadsheet: function() {
|
||||
var self = this,
|
||||
options = this.options,
|
||||
googleSpreadsheetKey = options.googleSpreadsheetKey,
|
||||
columns = this.columns,
|
||||
startRow = options.startRow || 0,
|
||||
endRow = options.endRow || Number.MAX_VALUE,
|
||||
startColumn = options.startColumn || 0,
|
||||
endColumn = options.endColumn || Number.MAX_VALUE,
|
||||
gr, // google row
|
||||
gc; // google column
|
||||
|
||||
if (googleSpreadsheetKey) {
|
||||
jQuery.ajax({
|
||||
dataType: 'json',
|
||||
url: 'https://spreadsheets.google.com/feeds/cells/' +
|
||||
googleSpreadsheetKey + '/' + (options.googleSpreadsheetWorksheet || 'od6') +
|
||||
'/public/values?alt=json-in-script&callback=?',
|
||||
error: options.error,
|
||||
success: function(json) {
|
||||
// Prepare the data from the spreadsheat
|
||||
var cells = json.feed.entry,
|
||||
cell,
|
||||
cellCount = cells.length,
|
||||
colCount = 0,
|
||||
rowCount = 0,
|
||||
i;
|
||||
|
||||
// First, find the total number of columns and rows that
|
||||
// are actually filled with data
|
||||
for (i = 0; i < cellCount; i++) {
|
||||
cell = cells[i];
|
||||
colCount = Math.max(colCount, cell.gs$cell.col);
|
||||
rowCount = Math.max(rowCount, cell.gs$cell.row);
|
||||
}
|
||||
|
||||
// Set up arrays containing the column data
|
||||
for (i = 0; i < colCount; i++) {
|
||||
if (i >= startColumn && i <= endColumn) {
|
||||
// Create new columns with the length of either end-start or rowCount
|
||||
columns[i - startColumn] = [];
|
||||
|
||||
// Setting the length to avoid jslint warning
|
||||
columns[i - startColumn].length = Math.min(rowCount, endRow - startRow);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the cells and assign the value to the right
|
||||
// place in the column arrays
|
||||
for (i = 0; i < cellCount; i++) {
|
||||
cell = cells[i];
|
||||
gr = cell.gs$cell.row - 1; // rows start at 1
|
||||
gc = cell.gs$cell.col - 1; // columns start at 1
|
||||
|
||||
// If both row and col falls inside start and end
|
||||
// set the transposed cell value in the newly created columns
|
||||
if (gc >= startColumn && gc <= endColumn &&
|
||||
gr >= startRow && gr <= endRow) {
|
||||
columns[gc - startColumn][gr - startRow] = cell.content.$t;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert null for empty spreadsheet cells (#5298)
|
||||
each(columns, function(column) {
|
||||
for (i = 0; i < column.length; i++) {
|
||||
if (column[i] === undefined) {
|
||||
column[i] = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self.dataFound();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Trim a string from whitespace
|
||||
*/
|
||||
trim: function(str, inside) {
|
||||
if (typeof str === 'string') {
|
||||
str = str.replace(/^\s+|\s+$/g, '');
|
||||
|
||||
// Clear white space insdie the string, like thousands separators
|
||||
if (inside && /^[0-9\s]+$/.test(str)) {
|
||||
str = str.replace(/\s/g, '');
|
||||
}
|
||||
|
||||
if (this.decimalRegex) {
|
||||
str = str.replace(this.decimalRegex, '$1.$2');
|
||||
}
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse numeric cells in to number types and date types in to true dates.
|
||||
*/
|
||||
parseTypes: function() {
|
||||
var columns = this.columns,
|
||||
col = columns.length;
|
||||
|
||||
while (col--) {
|
||||
this.parseColumn(columns[col], col);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a single column. Set properties like .isDatetime and .isNumeric.
|
||||
*/
|
||||
parseColumn: function(column, col) {
|
||||
var rawColumns = this.rawColumns,
|
||||
columns = this.columns,
|
||||
row = column.length,
|
||||
val,
|
||||
floatVal,
|
||||
trimVal,
|
||||
trimInsideVal,
|
||||
firstRowAsNames = this.firstRowAsNames,
|
||||
isXColumn = inArray(col, this.valueCount.xColumns) !== -1,
|
||||
dateVal,
|
||||
backup = [],
|
||||
diff,
|
||||
chartOptions = this.chartOptions,
|
||||
descending,
|
||||
columnTypes = this.options.columnTypes || [],
|
||||
columnType = columnTypes[col],
|
||||
forceCategory = isXColumn && ((chartOptions && chartOptions.xAxis && splat(chartOptions.xAxis)[0].type === 'category') || columnType === 'string');
|
||||
|
||||
if (!rawColumns[col]) {
|
||||
rawColumns[col] = [];
|
||||
}
|
||||
while (row--) {
|
||||
val = backup[row] || column[row];
|
||||
|
||||
trimVal = this.trim(val);
|
||||
trimInsideVal = this.trim(val, true);
|
||||
floatVal = parseFloat(trimInsideVal);
|
||||
|
||||
// Set it the first time
|
||||
if (rawColumns[col][row] === undefined) {
|
||||
rawColumns[col][row] = trimVal;
|
||||
}
|
||||
|
||||
// Disable number or date parsing by setting the X axis type to category
|
||||
if (forceCategory || (row === 0 && firstRowAsNames)) {
|
||||
column[row] = trimVal;
|
||||
|
||||
} else if (+trimInsideVal === floatVal) { // is numeric
|
||||
|
||||
column[row] = floatVal;
|
||||
|
||||
// If the number is greater than milliseconds in a year, assume datetime
|
||||
if (floatVal > 365 * 24 * 3600 * 1000 && columnType !== 'float') {
|
||||
column.isDatetime = true;
|
||||
} else {
|
||||
column.isNumeric = true;
|
||||
}
|
||||
|
||||
if (column[row + 1] !== undefined) {
|
||||
descending = floatVal > column[row + 1];
|
||||
}
|
||||
|
||||
// String, continue to determine if it is a date string or really a string
|
||||
} else {
|
||||
dateVal = this.parseDate(val);
|
||||
// Only allow parsing of dates if this column is an x-column
|
||||
if (isXColumn && isNumber(dateVal) && columnType !== 'float') { // is date
|
||||
backup[row] = val;
|
||||
column[row] = dateVal;
|
||||
column.isDatetime = true;
|
||||
|
||||
// Check if the dates are uniformly descending or ascending. If they
|
||||
// are not, chances are that they are a different time format, so check
|
||||
// for alternative.
|
||||
if (column[row + 1] !== undefined) {
|
||||
diff = dateVal > column[row + 1];
|
||||
if (diff !== descending && descending !== undefined) {
|
||||
if (this.alternativeFormat) {
|
||||
this.dateFormat = this.alternativeFormat;
|
||||
row = column.length;
|
||||
this.alternativeFormat = this.dateFormats[this.dateFormat].alternative;
|
||||
} else {
|
||||
column.unsorted = true;
|
||||
}
|
||||
}
|
||||
descending = diff;
|
||||
}
|
||||
|
||||
} else { // string
|
||||
column[row] = trimVal === '' ? null : trimVal;
|
||||
if (row !== 0 && (column.isDatetime || column.isNumeric)) {
|
||||
column.mixed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If strings are intermixed with numbers or dates in a parsed column, it is an indication
|
||||
// that parsing went wrong or the data was not intended to display as numbers or dates and
|
||||
// parsing is too aggressive. Fall back to categories. Demonstrated in the
|
||||
// highcharts/demo/column-drilldown sample.
|
||||
if (isXColumn && column.mixed) {
|
||||
columns[col] = rawColumns[col];
|
||||
}
|
||||
|
||||
// If the 0 column is date or number and descending, reverse all columns.
|
||||
if (isXColumn && descending && this.options.sort) {
|
||||
for (col = 0; col < columns.length; col++) {
|
||||
columns[col].reverse();
|
||||
if (firstRowAsNames) {
|
||||
columns[col].unshift(columns[col].pop());
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A collection of available date formats, extendable from the outside to support
|
||||
* custom date formats.
|
||||
*/
|
||||
dateFormats: {
|
||||
'YYYY-mm-dd': {
|
||||
regex: /^([0-9]{4})[\-\/\.]([0-9]{2})[\-\/\.]([0-9]{2})$/,
|
||||
parser: function(match) {
|
||||
return Date.UTC(+match[1], match[2] - 1, +match[3]);
|
||||
}
|
||||
},
|
||||
'dd/mm/YYYY': {
|
||||
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/,
|
||||
parser: function(match) {
|
||||
return Date.UTC(+match[3], match[2] - 1, +match[1]);
|
||||
},
|
||||
alternative: 'mm/dd/YYYY' // different format with the same regex
|
||||
},
|
||||
'mm/dd/YYYY': {
|
||||
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{4})$/,
|
||||
parser: function(match) {
|
||||
return Date.UTC(+match[3], match[1] - 1, +match[2]);
|
||||
}
|
||||
},
|
||||
'dd/mm/YY': {
|
||||
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/,
|
||||
parser: function(match) {
|
||||
return Date.UTC(+match[3] + 2000, match[2] - 1, +match[1]);
|
||||
},
|
||||
alternative: 'mm/dd/YY' // different format with the same regex
|
||||
},
|
||||
'mm/dd/YY': {
|
||||
regex: /^([0-9]{1,2})[\-\/\.]([0-9]{1,2})[\-\/\.]([0-9]{2})$/,
|
||||
parser: function(match) {
|
||||
return Date.UTC(+match[3] + 2000, match[1] - 1, +match[2]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a date and return it as a number. Overridable through options.parseDate.
|
||||
*/
|
||||
parseDate: function(val) {
|
||||
var parseDate = this.options.parseDate,
|
||||
ret,
|
||||
key,
|
||||
format,
|
||||
dateFormat = this.options.dateFormat || this.dateFormat,
|
||||
match;
|
||||
|
||||
if (parseDate) {
|
||||
ret = parseDate(val);
|
||||
|
||||
} else if (typeof val === 'string') {
|
||||
// Auto-detect the date format the first time
|
||||
if (!dateFormat) {
|
||||
for (key in this.dateFormats) {
|
||||
format = this.dateFormats[key];
|
||||
match = val.match(format.regex);
|
||||
if (match) {
|
||||
this.dateFormat = dateFormat = key;
|
||||
this.alternativeFormat = format.alternative;
|
||||
ret = format.parser(match);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Next time, use the one previously found
|
||||
} else {
|
||||
format = this.dateFormats[dateFormat];
|
||||
match = val.match(format.regex);
|
||||
if (match) {
|
||||
ret = format.parser(match);
|
||||
}
|
||||
}
|
||||
// Fall back to Date.parse
|
||||
if (!match) {
|
||||
match = Date.parse(val);
|
||||
// External tools like Date.js and MooTools extend Date object and
|
||||
// returns a date.
|
||||
if (typeof match === 'object' && match !== null && match.getTime) {
|
||||
ret = match.getTime() - match.getTimezoneOffset() * 60000;
|
||||
|
||||
// Timestamp
|
||||
} else if (isNumber(match)) {
|
||||
ret = match - (new Date(match)).getTimezoneOffset() * 60000;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reorganize rows into columns
|
||||
*/
|
||||
rowsToColumns: function(rows) {
|
||||
var row,
|
||||
rowsLength,
|
||||
col,
|
||||
colsLength,
|
||||
columns;
|
||||
|
||||
if (rows) {
|
||||
columns = [];
|
||||
rowsLength = rows.length;
|
||||
for (row = 0; row < rowsLength; row++) {
|
||||
colsLength = rows[row].length;
|
||||
for (col = 0; col < colsLength; col++) {
|
||||
if (!columns[col]) {
|
||||
columns[col] = [];
|
||||
}
|
||||
columns[col][row] = rows[row][col];
|
||||
}
|
||||
}
|
||||
}
|
||||
return columns;
|
||||
},
|
||||
|
||||
/**
|
||||
* A hook for working directly on the parsed columns
|
||||
*/
|
||||
parsed: function() {
|
||||
if (this.options.parsed) {
|
||||
return this.options.parsed.call(this, this.columns);
|
||||
}
|
||||
},
|
||||
|
||||
getFreeIndexes: function(numberOfColumns, seriesBuilders) {
|
||||
var s,
|
||||
i,
|
||||
freeIndexes = [],
|
||||
freeIndexValues = [],
|
||||
referencedIndexes;
|
||||
|
||||
// Add all columns as free
|
||||
for (i = 0; i < numberOfColumns; i = i + 1) {
|
||||
freeIndexes.push(true);
|
||||
}
|
||||
|
||||
// Loop all defined builders and remove their referenced columns
|
||||
for (s = 0; s < seriesBuilders.length; s = s + 1) {
|
||||
referencedIndexes = seriesBuilders[s].getReferencedColumnIndexes();
|
||||
|
||||
for (i = 0; i < referencedIndexes.length; i = i + 1) {
|
||||
freeIndexes[referencedIndexes[i]] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Collect the values for the free indexes
|
||||
for (i = 0; i < freeIndexes.length; i = i + 1) {
|
||||
if (freeIndexes[i]) {
|
||||
freeIndexValues.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
return freeIndexValues;
|
||||
},
|
||||
|
||||
/**
|
||||
* If a complete callback function is provided in the options, interpret the
|
||||
* columns into a Highcharts options object.
|
||||
*/
|
||||
complete: function() {
|
||||
|
||||
var columns = this.columns,
|
||||
xColumns = [],
|
||||
type,
|
||||
options = this.options,
|
||||
series,
|
||||
data,
|
||||
i,
|
||||
j,
|
||||
r,
|
||||
seriesIndex,
|
||||
chartOptions,
|
||||
allSeriesBuilders = [],
|
||||
builder,
|
||||
freeIndexes,
|
||||
typeCol,
|
||||
index;
|
||||
|
||||
xColumns.length = columns.length;
|
||||
if (options.complete || options.afterComplete) {
|
||||
|
||||
// Get the names and shift the top row
|
||||
for (i = 0; i < columns.length; i++) {
|
||||
if (this.firstRowAsNames) {
|
||||
columns[i].name = columns[i].shift();
|
||||
}
|
||||
}
|
||||
|
||||
// Use the next columns for series
|
||||
series = [];
|
||||
freeIndexes = this.getFreeIndexes(columns.length, this.valueCount.seriesBuilders);
|
||||
|
||||
// Populate defined series
|
||||
for (seriesIndex = 0; seriesIndex < this.valueCount.seriesBuilders.length; seriesIndex++) {
|
||||
builder = this.valueCount.seriesBuilders[seriesIndex];
|
||||
|
||||
// If the builder can be populated with remaining columns, then add it to allBuilders
|
||||
if (builder.populateColumns(freeIndexes)) {
|
||||
allSeriesBuilders.push(builder);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate dynamic series
|
||||
while (freeIndexes.length > 0) {
|
||||
builder = new SeriesBuilder();
|
||||
builder.addColumnReader(0, 'x');
|
||||
|
||||
// Mark index as used (not free)
|
||||
index = inArray(0, freeIndexes);
|
||||
if (index !== -1) {
|
||||
freeIndexes.splice(index, 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < this.valueCount.global; i++) {
|
||||
// Create and add a column reader for the next free column index
|
||||
builder.addColumnReader(undefined, this.valueCount.globalPointArrayMap[i]);
|
||||
}
|
||||
|
||||
// If the builder can be populated with remaining columns, then add it to allBuilders
|
||||
if (builder.populateColumns(freeIndexes)) {
|
||||
allSeriesBuilders.push(builder);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the data-type from the first series x column
|
||||
if (allSeriesBuilders.length > 0 && allSeriesBuilders[0].readers.length > 0) {
|
||||
typeCol = columns[allSeriesBuilders[0].readers[0].columnIndex];
|
||||
if (typeCol !== undefined) {
|
||||
if (typeCol.isDatetime) {
|
||||
type = 'datetime';
|
||||
} else if (!typeCol.isNumeric) {
|
||||
type = 'category';
|
||||
}
|
||||
}
|
||||
}
|
||||
// Axis type is category, then the "x" column should be called "name"
|
||||
if (type === 'category') {
|
||||
for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) {
|
||||
builder = allSeriesBuilders[seriesIndex];
|
||||
for (r = 0; r < builder.readers.length; r++) {
|
||||
if (builder.readers[r].configName === 'x') {
|
||||
builder.readers[r].configName = 'name';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read data for all builders
|
||||
for (seriesIndex = 0; seriesIndex < allSeriesBuilders.length; seriesIndex++) {
|
||||
builder = allSeriesBuilders[seriesIndex];
|
||||
|
||||
// Iterate down the cells of each column and add data to the series
|
||||
data = [];
|
||||
for (j = 0; j < columns[0].length; j++) {
|
||||
data[j] = builder.read(columns, j);
|
||||
}
|
||||
|
||||
// Add the series
|
||||
series[seriesIndex] = {
|
||||
data: data
|
||||
};
|
||||
if (builder.name) {
|
||||
series[seriesIndex].name = builder.name;
|
||||
}
|
||||
if (type === 'category') {
|
||||
series[seriesIndex].turboThreshold = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Do the callback
|
||||
chartOptions = {
|
||||
series: series
|
||||
};
|
||||
if (type) {
|
||||
chartOptions.xAxis = {
|
||||
type: type
|
||||
};
|
||||
if (type === 'category') {
|
||||
chartOptions.xAxis.nameToX = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.complete) {
|
||||
options.complete(chartOptions);
|
||||
}
|
||||
|
||||
// The afterComplete hook is used internally to avoid conflict with the externally
|
||||
// available complete option.
|
||||
if (options.afterComplete) {
|
||||
options.afterComplete(chartOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Register the Data prototype and data function on Highcharts
|
||||
Highcharts.Data = Data;
|
||||
Highcharts.data = function(options, chartOptions) {
|
||||
return new Data(options, chartOptions);
|
||||
};
|
||||
|
||||
// Extend Chart.init so that the Chart constructor accepts a new configuration
|
||||
// option group, data.
|
||||
Highcharts.wrap(Highcharts.Chart.prototype, 'init', function(proceed, userOptions, callback) {
|
||||
var chart = this;
|
||||
|
||||
if (userOptions && userOptions.data) {
|
||||
Highcharts.data(Highcharts.extend(userOptions.data, {
|
||||
|
||||
afterComplete: function(dataOptions) {
|
||||
var i, series;
|
||||
|
||||
// Merge series configs
|
||||
if (userOptions.hasOwnProperty('series')) {
|
||||
if (typeof userOptions.series === 'object') {
|
||||
i = Math.max(userOptions.series.length, dataOptions.series.length);
|
||||
while (i--) {
|
||||
series = userOptions.series[i] || {};
|
||||
userOptions.series[i] = Highcharts.merge(series, dataOptions.series[i]);
|
||||
}
|
||||
} else { // Allow merging in dataOptions.series (#2856)
|
||||
delete userOptions.series;
|
||||
}
|
||||
}
|
||||
|
||||
// Do the merge
|
||||
userOptions = Highcharts.merge(dataOptions, userOptions);
|
||||
|
||||
proceed.call(chart, userOptions, callback);
|
||||
}
|
||||
}), userOptions);
|
||||
} else {
|
||||
proceed.call(chart, userOptions, callback);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a new SeriesBuilder. A SeriesBuilder consists of a number
|
||||
* of ColumnReaders that reads columns and give them a name.
|
||||
* Ex: A series builder can be constructed to read column 3 as 'x' and
|
||||
* column 7 and 8 as 'y1' and 'y2'.
|
||||
* The output would then be points/rows of the form {x: 11, y1: 22, y2: 33}
|
||||
*
|
||||
* The name of the builder is taken from the second column. In the above
|
||||
* example it would be the column with index 7.
|
||||
* @constructor
|
||||
*/
|
||||
SeriesBuilder = function() {
|
||||
this.readers = [];
|
||||
this.pointIsArray = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Populates readers with column indexes. A reader can be added without
|
||||
* a specific index and for those readers the index is taken sequentially
|
||||
* from the free columns (this is handled by the ColumnCursor instance).
|
||||
* @returns {boolean}
|
||||
*/
|
||||
SeriesBuilder.prototype.populateColumns = function(freeIndexes) {
|
||||
var builder = this,
|
||||
enoughColumns = true;
|
||||
|
||||
// Loop each reader and give it an index if its missing.
|
||||
// The freeIndexes.shift() will return undefined if there
|
||||
// are no more columns.
|
||||
each(builder.readers, function(reader) {
|
||||
if (reader.columnIndex === undefined) {
|
||||
reader.columnIndex = freeIndexes.shift();
|
||||
}
|
||||
});
|
||||
|
||||
// Now, all readers should have columns mapped. If not
|
||||
// then return false to signal that this series should
|
||||
// not be added.
|
||||
each(builder.readers, function(reader) {
|
||||
if (reader.columnIndex === undefined) {
|
||||
enoughColumns = false;
|
||||
}
|
||||
});
|
||||
|
||||
return enoughColumns;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads a row from the dataset and returns a point or array depending
|
||||
* on the names of the readers.
|
||||
* @param columns
|
||||
* @param rowIndex
|
||||
* @returns {Array | Object}
|
||||
*/
|
||||
SeriesBuilder.prototype.read = function(columns, rowIndex) {
|
||||
var builder = this,
|
||||
pointIsArray = builder.pointIsArray,
|
||||
point = pointIsArray ? [] : {},
|
||||
columnIndexes;
|
||||
|
||||
// Loop each reader and ask it to read its value.
|
||||
// Then, build an array or point based on the readers names.
|
||||
each(builder.readers, function(reader) {
|
||||
var value = columns[reader.columnIndex][rowIndex];
|
||||
if (pointIsArray) {
|
||||
point.push(value);
|
||||
} else {
|
||||
point[reader.configName] = value;
|
||||
}
|
||||
});
|
||||
|
||||
// The name comes from the first column (excluding the x column)
|
||||
if (this.name === undefined && builder.readers.length >= 2) {
|
||||
columnIndexes = builder.getReferencedColumnIndexes();
|
||||
if (columnIndexes.length >= 2) {
|
||||
// remove the first one (x col)
|
||||
columnIndexes.shift();
|
||||
|
||||
// Sort the remaining
|
||||
columnIndexes.sort();
|
||||
|
||||
// Now use the lowest index as name column
|
||||
this.name = columns[columnIndexes.shift()].name;
|
||||
}
|
||||
}
|
||||
|
||||
return point;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and adds ColumnReader from the given columnIndex and configName.
|
||||
* ColumnIndex can be undefined and in that case the reader will be given
|
||||
* an index when columns are populated.
|
||||
* @param columnIndex {Number | undefined}
|
||||
* @param configName
|
||||
*/
|
||||
SeriesBuilder.prototype.addColumnReader = function(columnIndex, configName) {
|
||||
this.readers.push({
|
||||
columnIndex: columnIndex,
|
||||
configName: configName
|
||||
});
|
||||
|
||||
if (!(configName === 'x' || configName === 'y' || configName === undefined)) {
|
||||
this.pointIsArray = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an array of column indexes that the builder will use when
|
||||
* reading data.
|
||||
* @returns {Array}
|
||||
*/
|
||||
SeriesBuilder.prototype.getReferencedColumnIndexes = function() {
|
||||
var i,
|
||||
referencedColumnIndexes = [],
|
||||
columnReader;
|
||||
|
||||
for (i = 0; i < this.readers.length; i = i + 1) {
|
||||
columnReader = this.readers[i];
|
||||
if (columnReader.columnIndex !== undefined) {
|
||||
referencedColumnIndexes.push(columnReader.columnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return referencedColumnIndexes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the builder has a reader for the given configName.
|
||||
* @param configName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
SeriesBuilder.prototype.hasReader = function(configName) {
|
||||
var i, columnReader;
|
||||
for (i = 0; i < this.readers.length; i = i + 1) {
|
||||
columnReader = this.readers[i];
|
||||
if (columnReader.configName === configName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Else return undefined
|
||||
};
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
26
high/highcharts/js/modules/drilldown.js
Normal file
26
high/highcharts/js/modules/drilldown.js
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
Highcharts Drilldown module
|
||||
|
||||
Author: Torstein Honsi
|
||||
License: www.highcharts.com/license
|
||||
|
||||
*/
|
||||
(function(n){"object"===typeof module&&module.exports?module.exports=n:n(Highcharts)})(function(n){(function(e){function n(b,a,d){var c;a.rgba.length&&b.rgba.length?(b=b.rgba,a=a.rgba,c=1!==a[3]||1!==b[3],b=(c?"rgba(":"rgb(")+Math.round(a[0]+(b[0]-a[0])*(1-d))+","+Math.round(a[1]+(b[1]-a[1])*(1-d))+","+Math.round(a[2]+(b[2]-a[2])*(1-d))+(c?","+(a[3]+(b[3]-a[3])*(1-d)):"")+")"):b=a.input||"none";return b}var A=e.noop,B=e.color,u=e.defaultOptions,h=e.each,p=e.extend,H=e.format,C=e.pick,v=e.wrap,q=e.Chart,
|
||||
t=e.seriesTypes,D=t.pie,r=t.column,E=e.Tick,w=e.fireEvent,F=e.inArray,G=1;h(["fill","stroke"],function(b){e.Fx.prototype[b+"Setter"]=function(){this.elem.attr(b,n(B(this.start),B(this.end),this.pos))}});p(u.lang,{drillUpText:"\u25c1 Back to {series.name}"});u.drilldown={activeAxisLabelStyle:{cursor:"pointer",color:"#003399",fontWeight:"bold",textDecoration:"underline"},activeDataLabelStyle:{cursor:"pointer",color:"#003399",fontWeight:"bold",textDecoration:"underline"},animation:{duration:500},drillUpButton:{position:{align:"right",
|
||||
x:-10,y:10}}};e.SVGRenderer.prototype.Element.prototype.fadeIn=function(b){this.attr({opacity:.1,visibility:"inherit"}).animate({opacity:C(this.newOpacity,1)},b||{duration:250})};q.prototype.addSeriesAsDrilldown=function(b,a){this.addSingleSeriesAsDrilldown(b,a);this.applyDrilldown()};q.prototype.addSingleSeriesAsDrilldown=function(b,a){var d=b.series,c=d.xAxis,g=d.yAxis,f,k=[],x=[],e,m,y;y={color:b.color||d.color};this.drilldownLevels||(this.drilldownLevels=[]);e=d.options._levelNumber||0;(m=this.drilldownLevels[this.drilldownLevels.length-
|
||||
1])&&m.levelNumber!==e&&(m=void 0);a=p(p({_ddSeriesId:G++},y),a);f=F(b,d.points);h(d.chart.series,function(a){a.xAxis!==c||a.isDrilling||(a.options._ddSeriesId=a.options._ddSeriesId||G++,a.options._colorIndex=a.userOptions._colorIndex,a.options._levelNumber=a.options._levelNumber||e,m?(k=m.levelSeries,x=m.levelSeriesOptions):(k.push(a),x.push(a.options)))});f=p({levelNumber:e,seriesOptions:d.options,levelSeriesOptions:x,levelSeries:k,shapeArgs:b.shapeArgs,bBox:b.graphic?b.graphic.getBBox():{},lowerSeriesOptions:a,
|
||||
pointOptions:d.options.data[f],pointIndex:f,oldExtremes:{xMin:c&&c.userMin,xMax:c&&c.userMax,yMin:g&&g.userMin,yMax:g&&g.userMax}},y);this.drilldownLevels.push(f);f=f.lowerSeries=this.addSeries(a,!1);f.options._levelNumber=e+1;c&&(c.oldPos=c.pos,c.userMin=c.userMax=null,g.userMin=g.userMax=null);d.type===f.type&&(f.animate=f.animateDrilldown||A,f.options.animation=!0)};q.prototype.applyDrilldown=function(){var b=this.drilldownLevels,a;b&&0<b.length&&(a=b[b.length-1].levelNumber,h(this.drilldownLevels,
|
||||
function(b){b.levelNumber===a&&h(b.levelSeries,function(c){c.options&&c.options._levelNumber===a&&c.remove(!1)})}));this.redraw();this.showDrillUpButton()};q.prototype.getDrilldownBackText=function(){var b=this.drilldownLevels;if(b&&0<b.length)return b=b[b.length-1],b.series=b.seriesOptions,H(this.options.lang.drillUpText,b)};q.prototype.showDrillUpButton=function(){var b=this,a=this.getDrilldownBackText(),d=b.options.drilldown.drillUpButton,c,g;this.drillUpButton?this.drillUpButton.attr({text:a}).align():
|
||||
(g=(c=d.theme)&&c.states,this.drillUpButton=this.renderer.button(a,null,null,function(){b.drillUp()},c,g&&g.hover,g&&g.select).addClass("highcharts-drillup-button").attr({align:d.position.align,zIndex:7}).add().align(d.position,!1,d.relativeTo||"plotBox"))};q.prototype.drillUp=function(){for(var b=this,a=b.drilldownLevels,d=a[a.length-1].levelNumber,c=a.length,g=b.series,f,k,e,l,m=function(a){var c;h(g,function(b){b.options._ddSeriesId===a._ddSeriesId&&(c=b)});c=c||b.addSeries(a,!1);c.type===e.type&&
|
||||
c.animateDrillupTo&&(c.animate=c.animateDrillupTo);a===k.seriesOptions&&(l=c)};c--;)if(k=a[c],k.levelNumber===d){a.pop();e=k.lowerSeries;if(!e.chart)for(f=g.length;f--;)if(g[f].options.id===k.lowerSeriesOptions.id&&g[f].options._levelNumber===d+1){e=g[f];break}e.xData=[];h(k.levelSeriesOptions,m);w(b,"drillup",{seriesOptions:k.seriesOptions});l.type===e.type&&(l.drilldownLevel=k,l.options.animation=b.options.drilldown.animation,e.animateDrillupFrom&&e.chart&&e.animateDrillupFrom(k));l.options._levelNumber=
|
||||
d;e.remove(!1);l.xAxis&&(f=k.oldExtremes,l.xAxis.setExtremes(f.xMin,f.xMax,!1),l.yAxis.setExtremes(f.yMin,f.yMax,!1))}w(b,"drillupall");this.redraw();0===this.drilldownLevels.length?this.drillUpButton=this.drillUpButton.destroy():this.drillUpButton.attr({text:this.getDrilldownBackText()}).align();this.ddDupes.length=[]};r.prototype.supportsDrilldown=!0;r.prototype.animateDrillupTo=function(b){if(!b){var a=this,d=a.drilldownLevel;h(this.points,function(a){a.graphic&&a.graphic.hide();a.dataLabel&&a.dataLabel.hide();
|
||||
a.connector&&a.connector.hide()});setTimeout(function(){a.points&&h(a.points,function(a,b){var f=b===(d&&d.pointIndex)?"show":"fadeIn",k="show"===f?!0:void 0;if(a.graphic)a.graphic[f](k);if(a.dataLabel)a.dataLabel[f](k);if(a.connector)a.connector[f](k)})},Math.max(this.chart.options.drilldown.animation.duration-50,0));this.animate=A}};r.prototype.animateDrilldown=function(b){var a=this,d=this.chart.drilldownLevels,c,g=this.chart.options.drilldown.animation,f=this.xAxis;b||(h(d,function(b){a.options._ddSeriesId===
|
||||
b.lowerSeriesOptions._ddSeriesId&&(c=b.shapeArgs,c.fill=b.color)}),c.x+=C(f.oldPos,f.pos)-f.pos,h(this.points,function(b){b.shapeArgs.fill=b.color;b.graphic&&b.graphic.attr(c).animate(p(b.shapeArgs,{fill:b.color||a.color}),g);b.dataLabel&&b.dataLabel.fadeIn(g)}),this.animate=null)};r.prototype.animateDrillupFrom=function(b){var a=this.chart.options.drilldown.animation,d=this.group,c=this;h(c.trackerGroups,function(a){if(c[a])c[a].on("mouseover")});delete this.group;h(this.points,function(c){var f=
|
||||
c.graphic,k=b.shapeArgs,h=function(){f.destroy();d&&(d=d.destroy())};f&&(delete c.graphic,k.fill=b.color,a?f.animate(k,e.merge(a,{complete:h})):(f.attr(k),h()))})};D&&p(D.prototype,{supportsDrilldown:!0,animateDrillupTo:r.prototype.animateDrillupTo,animateDrillupFrom:r.prototype.animateDrillupFrom,animateDrilldown:function(b){var a=this.chart.drilldownLevels[this.chart.drilldownLevels.length-1],d=this.chart.options.drilldown.animation,c=a.shapeArgs,g=c.start,f=(c.end-g)/this.points.length;b||(h(this.points,
|
||||
function(b,h){var l=b.shapeArgs;c.fill=a.color;l.fill=b.color;if(b.graphic)b.graphic.attr(e.merge(c,{start:g+h*f,end:g+(h+1)*f}))[d?"animate":"attr"](l,d)}),this.animate=null)}});e.Point.prototype.doDrilldown=function(b,a,d){var c=this.series.chart,g=c.options.drilldown,f=(g.series||[]).length,e;c.ddDupes||(c.ddDupes=[]);for(;f--&&!e;)g.series[f].id===this.drilldown&&-1===F(this.drilldown,c.ddDupes)&&(e=g.series[f],c.ddDupes.push(this.drilldown));w(c,"drilldown",{point:this,seriesOptions:e,category:a,
|
||||
originalEvent:d,points:void 0!==a&&this.series.xAxis.getDDPoints(a).slice(0)},function(a){var c=a.point.series&&a.point.series.chart,d=a.seriesOptions;c&&d&&(b?c.addSingleSeriesAsDrilldown(a.point,d):c.addSeriesAsDrilldown(a.point,d))})};e.Axis.prototype.drilldownCategory=function(b,a){var d,c,e=this.getDDPoints(b);for(d in e)(c=e[d])&&c.series&&c.series.visible&&c.doDrilldown&&c.doDrilldown(!0,b,a);this.chart.applyDrilldown()};e.Axis.prototype.getDDPoints=function(b){var a=[];h(this.series,function(d){var c,
|
||||
e=d.xData,f=d.points;for(c=0;c<e.length;c++)if(e[c]===b&&d.options.data[c].drilldown){a.push(f?f[c]:!0);break}});return a};E.prototype.drillable=function(){var b=this.pos,a=this.label,d=this.axis,c="xAxis"===d.coll&&d.getDDPoints,g=c&&d.getDDPoints(b);c&&(a&&g.length?(a.drillable=!0,a.basicStyles||(a.basicStyles=e.merge(a.styles)),a.addClass("highcharts-drilldown-axis-label").css(d.chart.options.drilldown.activeAxisLabelStyle).on("click",function(a){d.drilldownCategory(b,a)})):a&&a.drillable&&(a.styles=
|
||||
{},a.css(a.basicStyles),a.on("click",null),a.removeClass("highcharts-drilldown-axis-label")))};v(E.prototype,"addLabel",function(b){b.call(this);this.drillable()});v(e.Point.prototype,"init",function(b,a,d,c){var g=b.call(this,a,d,c);b=(b=a.xAxis)&&b.ticks[c];g.drilldown&&e.addEvent(g,"click",function(b){a.xAxis&&!1===a.chart.options.drilldown.allowPointDrilldown?a.xAxis.drilldownCategory(c,b):g.doDrilldown(void 0,void 0,b)});b&&b.drillable();return g});v(e.Series.prototype,"drawDataLabels",function(b){var a=
|
||||
this.chart.options.drilldown.activeDataLabelStyle,d=this.chart.renderer;b.call(this);h(this.points,function(b){var e={};b.drilldown&&b.dataLabel&&("contrast"===a.color&&(e.color=d.getContrast(b.color||this.color)),b.dataLabel.addClass("highcharts-drilldown-data-label"),b.dataLabel.css(a).css(e))},this)});var z,u=function(b){b.call(this);h(this.points,function(a){a.drilldown&&a.graphic&&(a.graphic.addClass("highcharts-drilldown-point"),a.graphic.css({cursor:"pointer"}))})};for(z in t)t[z].prototype.supportsDrilldown&&
|
||||
v(t[z].prototype,"drawTracker",u)})(n)});
|
791
high/highcharts/js/modules/drilldown.src.js
Normal file
791
high/highcharts/js/modules/drilldown.src.js
Normal file
@ -0,0 +1,791 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
* Highcharts Drilldown module
|
||||
*
|
||||
* Author: Torstein Honsi
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* Highcharts Drilldown module
|
||||
*
|
||||
* Author: Torstein Honsi
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var noop = H.noop,
|
||||
color = H.color,
|
||||
defaultOptions = H.defaultOptions,
|
||||
each = H.each,
|
||||
extend = H.extend,
|
||||
format = H.format,
|
||||
pick = H.pick,
|
||||
wrap = H.wrap,
|
||||
Chart = H.Chart,
|
||||
seriesTypes = H.seriesTypes,
|
||||
PieSeries = seriesTypes.pie,
|
||||
ColumnSeries = seriesTypes.column,
|
||||
Tick = H.Tick,
|
||||
fireEvent = H.fireEvent,
|
||||
inArray = H.inArray,
|
||||
ddSeriesId = 1;
|
||||
|
||||
// Utilities
|
||||
/*
|
||||
* Return an intermediate color between two colors, according to pos where 0
|
||||
* is the from color and 1 is the to color. This method is copied from ColorAxis.js
|
||||
* and should always be kept updated, until we get AMD support.
|
||||
*/
|
||||
function tweenColors(from, to, pos) {
|
||||
// Check for has alpha, because rgba colors perform worse due to lack of
|
||||
// support in WebKit.
|
||||
var hasAlpha,
|
||||
ret;
|
||||
|
||||
// Unsupported color, return to-color (#3920)
|
||||
if (!to.rgba.length || !from.rgba.length) {
|
||||
ret = to.input || 'none';
|
||||
|
||||
// Interpolate
|
||||
} else {
|
||||
from = from.rgba;
|
||||
to = to.rgba;
|
||||
hasAlpha = (to[3] !== 1 || from[3] !== 1);
|
||||
ret = (hasAlpha ? 'rgba(' : 'rgb(') +
|
||||
Math.round(to[0] + (from[0] - to[0]) * (1 - pos)) + ',' +
|
||||
Math.round(to[1] + (from[1] - to[1]) * (1 - pos)) + ',' +
|
||||
Math.round(to[2] + (from[2] - to[2]) * (1 - pos)) +
|
||||
(hasAlpha ? (',' + (to[3] + (from[3] - to[3]) * (1 - pos))) : '') + ')';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Handle animation of the color attributes directly
|
||||
*/
|
||||
each(['fill', 'stroke'], function(prop) {
|
||||
H.Fx.prototype[prop + 'Setter'] = function() {
|
||||
this.elem.attr(prop, tweenColors(color(this.start), color(this.end), this.pos));
|
||||
};
|
||||
});
|
||||
|
||||
// Add language
|
||||
extend(defaultOptions.lang, {
|
||||
drillUpText: '◁ Back to {series.name}'
|
||||
});
|
||||
defaultOptions.drilldown = {
|
||||
|
||||
activeAxisLabelStyle: {
|
||||
cursor: 'pointer',
|
||||
color: '#003399',
|
||||
fontWeight: 'bold',
|
||||
textDecoration: 'underline'
|
||||
},
|
||||
activeDataLabelStyle: {
|
||||
cursor: 'pointer',
|
||||
color: '#003399',
|
||||
fontWeight: 'bold',
|
||||
textDecoration: 'underline'
|
||||
},
|
||||
|
||||
animation: {
|
||||
duration: 500
|
||||
},
|
||||
drillUpButton: {
|
||||
position: {
|
||||
align: 'right',
|
||||
x: -10,
|
||||
y: 10
|
||||
}
|
||||
// relativeTo: 'plotBox'
|
||||
// theme
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A general fadeIn method
|
||||
*/
|
||||
H.SVGRenderer.prototype.Element.prototype.fadeIn = function(animation) {
|
||||
this
|
||||
.attr({
|
||||
opacity: 0.1,
|
||||
visibility: 'inherit'
|
||||
})
|
||||
.animate({
|
||||
opacity: pick(this.newOpacity, 1) // newOpacity used in maps
|
||||
}, animation || {
|
||||
duration: 250
|
||||
});
|
||||
};
|
||||
|
||||
Chart.prototype.addSeriesAsDrilldown = function(point, ddOptions) {
|
||||
this.addSingleSeriesAsDrilldown(point, ddOptions);
|
||||
this.applyDrilldown();
|
||||
};
|
||||
Chart.prototype.addSingleSeriesAsDrilldown = function(point, ddOptions) {
|
||||
var oldSeries = point.series,
|
||||
xAxis = oldSeries.xAxis,
|
||||
yAxis = oldSeries.yAxis,
|
||||
newSeries,
|
||||
pointIndex,
|
||||
levelSeries = [],
|
||||
levelSeriesOptions = [],
|
||||
level,
|
||||
levelNumber,
|
||||
last,
|
||||
colorProp;
|
||||
|
||||
|
||||
|
||||
colorProp = {
|
||||
color: point.color || oldSeries.color
|
||||
};
|
||||
|
||||
|
||||
if (!this.drilldownLevels) {
|
||||
this.drilldownLevels = [];
|
||||
}
|
||||
|
||||
levelNumber = oldSeries.options._levelNumber || 0;
|
||||
|
||||
// See if we can reuse the registered series from last run
|
||||
last = this.drilldownLevels[this.drilldownLevels.length - 1];
|
||||
if (last && last.levelNumber !== levelNumber) {
|
||||
last = undefined;
|
||||
}
|
||||
|
||||
ddOptions = extend(extend({
|
||||
_ddSeriesId: ddSeriesId++
|
||||
}, colorProp), ddOptions);
|
||||
pointIndex = inArray(point, oldSeries.points);
|
||||
|
||||
// Record options for all current series
|
||||
each(oldSeries.chart.series, function(series) {
|
||||
if (series.xAxis === xAxis && !series.isDrilling) {
|
||||
series.options._ddSeriesId = series.options._ddSeriesId || ddSeriesId++;
|
||||
series.options._colorIndex = series.userOptions._colorIndex;
|
||||
series.options._levelNumber = series.options._levelNumber || levelNumber; // #3182
|
||||
|
||||
if (last) {
|
||||
levelSeries = last.levelSeries;
|
||||
levelSeriesOptions = last.levelSeriesOptions;
|
||||
} else {
|
||||
levelSeries.push(series);
|
||||
levelSeriesOptions.push(series.options);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add a record of properties for each drilldown level
|
||||
level = extend({
|
||||
levelNumber: levelNumber,
|
||||
seriesOptions: oldSeries.options,
|
||||
levelSeriesOptions: levelSeriesOptions,
|
||||
levelSeries: levelSeries,
|
||||
shapeArgs: point.shapeArgs,
|
||||
bBox: point.graphic ? point.graphic.getBBox() : {}, // no graphic in line series with markers disabled
|
||||
lowerSeriesOptions: ddOptions,
|
||||
pointOptions: oldSeries.options.data[pointIndex],
|
||||
pointIndex: pointIndex,
|
||||
oldExtremes: {
|
||||
xMin: xAxis && xAxis.userMin,
|
||||
xMax: xAxis && xAxis.userMax,
|
||||
yMin: yAxis && yAxis.userMin,
|
||||
yMax: yAxis && yAxis.userMax
|
||||
}
|
||||
}, colorProp);
|
||||
|
||||
// Push it to the lookup array
|
||||
this.drilldownLevels.push(level);
|
||||
|
||||
newSeries = level.lowerSeries = this.addSeries(ddOptions, false);
|
||||
newSeries.options._levelNumber = levelNumber + 1;
|
||||
if (xAxis) {
|
||||
xAxis.oldPos = xAxis.pos;
|
||||
xAxis.userMin = xAxis.userMax = null;
|
||||
yAxis.userMin = yAxis.userMax = null;
|
||||
}
|
||||
|
||||
// Run fancy cross-animation on supported and equal types
|
||||
if (oldSeries.type === newSeries.type) {
|
||||
newSeries.animate = newSeries.animateDrilldown || noop;
|
||||
newSeries.options.animation = true;
|
||||
}
|
||||
};
|
||||
|
||||
Chart.prototype.applyDrilldown = function() {
|
||||
var drilldownLevels = this.drilldownLevels,
|
||||
levelToRemove;
|
||||
|
||||
if (drilldownLevels && drilldownLevels.length > 0) { // #3352, async loading
|
||||
levelToRemove = drilldownLevels[drilldownLevels.length - 1].levelNumber;
|
||||
each(this.drilldownLevels, function(level) {
|
||||
if (level.levelNumber === levelToRemove) {
|
||||
each(level.levelSeries, function(series) {
|
||||
if (series.options && series.options._levelNumber === levelToRemove) { // Not removed, not added as part of a multi-series drilldown
|
||||
series.remove(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.redraw();
|
||||
this.showDrillUpButton();
|
||||
};
|
||||
|
||||
Chart.prototype.getDrilldownBackText = function() {
|
||||
var drilldownLevels = this.drilldownLevels,
|
||||
lastLevel;
|
||||
if (drilldownLevels && drilldownLevels.length > 0) { // #3352, async loading
|
||||
lastLevel = drilldownLevels[drilldownLevels.length - 1];
|
||||
lastLevel.series = lastLevel.seriesOptions;
|
||||
return format(this.options.lang.drillUpText, lastLevel);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Chart.prototype.showDrillUpButton = function() {
|
||||
var chart = this,
|
||||
backText = this.getDrilldownBackText(),
|
||||
buttonOptions = chart.options.drilldown.drillUpButton,
|
||||
attr,
|
||||
states;
|
||||
|
||||
|
||||
if (!this.drillUpButton) {
|
||||
attr = buttonOptions.theme;
|
||||
states = attr && attr.states;
|
||||
|
||||
this.drillUpButton = this.renderer.button(
|
||||
backText,
|
||||
null,
|
||||
null,
|
||||
function() {
|
||||
chart.drillUp();
|
||||
},
|
||||
attr,
|
||||
states && states.hover,
|
||||
states && states.select
|
||||
)
|
||||
.addClass('highcharts-drillup-button')
|
||||
.attr({
|
||||
align: buttonOptions.position.align,
|
||||
zIndex: 7
|
||||
})
|
||||
.add()
|
||||
.align(buttonOptions.position, false, buttonOptions.relativeTo || 'plotBox');
|
||||
} else {
|
||||
this.drillUpButton.attr({
|
||||
text: backText
|
||||
})
|
||||
.align();
|
||||
}
|
||||
};
|
||||
|
||||
Chart.prototype.drillUp = function() {
|
||||
var chart = this,
|
||||
drilldownLevels = chart.drilldownLevels,
|
||||
levelNumber = drilldownLevels[drilldownLevels.length - 1].levelNumber,
|
||||
i = drilldownLevels.length,
|
||||
chartSeries = chart.series,
|
||||
seriesI,
|
||||
level,
|
||||
oldSeries,
|
||||
newSeries,
|
||||
oldExtremes,
|
||||
addSeries = function(seriesOptions) {
|
||||
var addedSeries;
|
||||
each(chartSeries, function(series) {
|
||||
if (series.options._ddSeriesId === seriesOptions._ddSeriesId) {
|
||||
addedSeries = series;
|
||||
}
|
||||
});
|
||||
|
||||
addedSeries = addedSeries || chart.addSeries(seriesOptions, false);
|
||||
if (addedSeries.type === oldSeries.type && addedSeries.animateDrillupTo) {
|
||||
addedSeries.animate = addedSeries.animateDrillupTo;
|
||||
}
|
||||
if (seriesOptions === level.seriesOptions) {
|
||||
newSeries = addedSeries;
|
||||
}
|
||||
};
|
||||
|
||||
while (i--) {
|
||||
|
||||
level = drilldownLevels[i];
|
||||
if (level.levelNumber === levelNumber) {
|
||||
drilldownLevels.pop();
|
||||
|
||||
// Get the lower series by reference or id
|
||||
oldSeries = level.lowerSeries;
|
||||
if (!oldSeries.chart) { // #2786
|
||||
seriesI = chartSeries.length; // #2919
|
||||
while (seriesI--) {
|
||||
if (chartSeries[seriesI].options.id === level.lowerSeriesOptions.id &&
|
||||
chartSeries[seriesI].options._levelNumber === levelNumber + 1) { // #3867
|
||||
oldSeries = chartSeries[seriesI];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
oldSeries.xData = []; // Overcome problems with minRange (#2898)
|
||||
|
||||
each(level.levelSeriesOptions, addSeries);
|
||||
|
||||
fireEvent(chart, 'drillup', {
|
||||
seriesOptions: level.seriesOptions
|
||||
});
|
||||
|
||||
if (newSeries.type === oldSeries.type) {
|
||||
newSeries.drilldownLevel = level;
|
||||
newSeries.options.animation = chart.options.drilldown.animation;
|
||||
|
||||
if (oldSeries.animateDrillupFrom && oldSeries.chart) { // #2919
|
||||
oldSeries.animateDrillupFrom(level);
|
||||
}
|
||||
}
|
||||
newSeries.options._levelNumber = levelNumber;
|
||||
|
||||
oldSeries.remove(false);
|
||||
|
||||
// Reset the zoom level of the upper series
|
||||
if (newSeries.xAxis) {
|
||||
oldExtremes = level.oldExtremes;
|
||||
newSeries.xAxis.setExtremes(oldExtremes.xMin, oldExtremes.xMax, false);
|
||||
newSeries.yAxis.setExtremes(oldExtremes.yMin, oldExtremes.yMax, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fire a once-off event after all series have been drilled up (#5158)
|
||||
fireEvent(chart, 'drillupall');
|
||||
|
||||
this.redraw();
|
||||
|
||||
if (this.drilldownLevels.length === 0) {
|
||||
this.drillUpButton = this.drillUpButton.destroy();
|
||||
} else {
|
||||
this.drillUpButton.attr({
|
||||
text: this.getDrilldownBackText()
|
||||
})
|
||||
.align();
|
||||
}
|
||||
|
||||
this.ddDupes.length = []; // #3315
|
||||
};
|
||||
|
||||
|
||||
ColumnSeries.prototype.supportsDrilldown = true;
|
||||
|
||||
/**
|
||||
* When drilling up, keep the upper series invisible until the lower series has
|
||||
* moved into place
|
||||
*/
|
||||
ColumnSeries.prototype.animateDrillupTo = function(init) {
|
||||
if (!init) {
|
||||
var newSeries = this,
|
||||
level = newSeries.drilldownLevel;
|
||||
|
||||
each(this.points, function(point) {
|
||||
if (point.graphic) { // #3407
|
||||
point.graphic.hide();
|
||||
}
|
||||
if (point.dataLabel) {
|
||||
point.dataLabel.hide();
|
||||
}
|
||||
if (point.connector) {
|
||||
point.connector.hide();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Do dummy animation on first point to get to complete
|
||||
setTimeout(function() {
|
||||
if (newSeries.points) { // May be destroyed in the meantime, #3389
|
||||
each(newSeries.points, function(point, i) {
|
||||
// Fade in other points
|
||||
var verb = i === (level && level.pointIndex) ? 'show' : 'fadeIn',
|
||||
inherit = verb === 'show' ? true : undefined;
|
||||
if (point.graphic) { // #3407
|
||||
point.graphic[verb](inherit);
|
||||
}
|
||||
if (point.dataLabel) {
|
||||
point.dataLabel[verb](inherit);
|
||||
}
|
||||
if (point.connector) {
|
||||
point.connector[verb](inherit);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, Math.max(this.chart.options.drilldown.animation.duration - 50, 0));
|
||||
|
||||
// Reset
|
||||
this.animate = noop;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ColumnSeries.prototype.animateDrilldown = function(init) {
|
||||
var series = this,
|
||||
drilldownLevels = this.chart.drilldownLevels,
|
||||
animateFrom,
|
||||
animationOptions = this.chart.options.drilldown.animation,
|
||||
xAxis = this.xAxis;
|
||||
|
||||
if (!init) {
|
||||
each(drilldownLevels, function(level) {
|
||||
if (series.options._ddSeriesId === level.lowerSeriesOptions._ddSeriesId) {
|
||||
animateFrom = level.shapeArgs;
|
||||
|
||||
// Add the point colors to animate from
|
||||
animateFrom.fill = level.color;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
animateFrom.x += (pick(xAxis.oldPos, xAxis.pos) - xAxis.pos);
|
||||
|
||||
each(this.points, function(point) {
|
||||
var animateTo = point.shapeArgs;
|
||||
|
||||
|
||||
// Add the point colors to animate to
|
||||
animateTo.fill = point.color;
|
||||
|
||||
|
||||
if (point.graphic) {
|
||||
point.graphic
|
||||
.attr(animateFrom)
|
||||
.animate(
|
||||
extend(point.shapeArgs, {
|
||||
fill: point.color || series.color
|
||||
}),
|
||||
animationOptions
|
||||
);
|
||||
}
|
||||
if (point.dataLabel) {
|
||||
point.dataLabel.fadeIn(animationOptions);
|
||||
}
|
||||
});
|
||||
this.animate = null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* When drilling up, pull out the individual point graphics from the lower series
|
||||
* and animate them into the origin point in the upper series.
|
||||
*/
|
||||
ColumnSeries.prototype.animateDrillupFrom = function(level) {
|
||||
var animationOptions = this.chart.options.drilldown.animation,
|
||||
group = this.group,
|
||||
series = this;
|
||||
|
||||
// Cancel mouse events on the series group (#2787)
|
||||
each(series.trackerGroups, function(key) {
|
||||
if (series[key]) { // we don't always have dataLabelsGroup
|
||||
series[key].on('mouseover');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
delete this.group;
|
||||
each(this.points, function(point) {
|
||||
var graphic = point.graphic,
|
||||
animateTo = level.shapeArgs,
|
||||
complete = function() {
|
||||
graphic.destroy();
|
||||
if (group) {
|
||||
group = group.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
if (graphic) {
|
||||
|
||||
delete point.graphic;
|
||||
|
||||
|
||||
animateTo.fill = level.color;
|
||||
|
||||
|
||||
if (animationOptions) {
|
||||
graphic.animate(
|
||||
animateTo,
|
||||
H.merge(animationOptions, {
|
||||
complete: complete
|
||||
})
|
||||
);
|
||||
} else {
|
||||
graphic.attr(animateTo);
|
||||
complete();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (PieSeries) {
|
||||
extend(PieSeries.prototype, {
|
||||
supportsDrilldown: true,
|
||||
animateDrillupTo: ColumnSeries.prototype.animateDrillupTo,
|
||||
animateDrillupFrom: ColumnSeries.prototype.animateDrillupFrom,
|
||||
|
||||
animateDrilldown: function(init) {
|
||||
var level = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1],
|
||||
animationOptions = this.chart.options.drilldown.animation,
|
||||
animateFrom = level.shapeArgs,
|
||||
start = animateFrom.start,
|
||||
angle = animateFrom.end - start,
|
||||
startAngle = angle / this.points.length;
|
||||
|
||||
if (!init) {
|
||||
each(this.points, function(point, i) {
|
||||
var animateTo = point.shapeArgs;
|
||||
|
||||
|
||||
animateFrom.fill = level.color;
|
||||
animateTo.fill = point.color;
|
||||
|
||||
|
||||
if (point.graphic) {
|
||||
point.graphic
|
||||
.attr(H.merge(animateFrom, {
|
||||
start: start + i * startAngle,
|
||||
end: start + (i + 1) * startAngle
|
||||
}))[animationOptions ? 'animate' : 'attr'](
|
||||
animateTo,
|
||||
animationOptions
|
||||
);
|
||||
}
|
||||
});
|
||||
this.animate = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
H.Point.prototype.doDrilldown = function(_holdRedraw, category, originalEvent) {
|
||||
var series = this.series,
|
||||
chart = series.chart,
|
||||
drilldown = chart.options.drilldown,
|
||||
i = (drilldown.series || []).length,
|
||||
seriesOptions;
|
||||
|
||||
if (!chart.ddDupes) {
|
||||
chart.ddDupes = [];
|
||||
}
|
||||
|
||||
while (i-- && !seriesOptions) {
|
||||
if (drilldown.series[i].id === this.drilldown && inArray(this.drilldown, chart.ddDupes) === -1) {
|
||||
seriesOptions = drilldown.series[i];
|
||||
chart.ddDupes.push(this.drilldown);
|
||||
}
|
||||
}
|
||||
|
||||
// Fire the event. If seriesOptions is undefined, the implementer can check for
|
||||
// seriesOptions, and call addSeriesAsDrilldown async if necessary.
|
||||
fireEvent(chart, 'drilldown', {
|
||||
point: this,
|
||||
seriesOptions: seriesOptions,
|
||||
category: category,
|
||||
originalEvent: originalEvent,
|
||||
points: category !== undefined && this.series.xAxis.getDDPoints(category).slice(0)
|
||||
}, function(e) {
|
||||
var chart = e.point.series && e.point.series.chart,
|
||||
seriesOptions = e.seriesOptions;
|
||||
if (chart && seriesOptions) {
|
||||
if (_holdRedraw) {
|
||||
chart.addSingleSeriesAsDrilldown(e.point, seriesOptions);
|
||||
} else {
|
||||
chart.addSeriesAsDrilldown(e.point, seriesOptions);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Drill down to a given category. This is the same as clicking on an axis label.
|
||||
*/
|
||||
H.Axis.prototype.drilldownCategory = function(x, e) {
|
||||
var key,
|
||||
point,
|
||||
ddPointsX = this.getDDPoints(x);
|
||||
for (key in ddPointsX) {
|
||||
point = ddPointsX[key];
|
||||
if (point && point.series && point.series.visible && point.doDrilldown) { // #3197
|
||||
point.doDrilldown(true, x, e);
|
||||
}
|
||||
}
|
||||
this.chart.applyDrilldown();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return drillable points for this specific X value
|
||||
*/
|
||||
H.Axis.prototype.getDDPoints = function(x) {
|
||||
var ret = [];
|
||||
each(this.series, function(series) {
|
||||
var i,
|
||||
xData = series.xData,
|
||||
points = series.points;
|
||||
|
||||
for (i = 0; i < xData.length; i++) {
|
||||
if (xData[i] === x && series.options.data[i].drilldown) {
|
||||
ret.push(points ? points[i] : true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Make a tick label drillable, or remove drilling on update
|
||||
*/
|
||||
Tick.prototype.drillable = function() {
|
||||
var pos = this.pos,
|
||||
label = this.label,
|
||||
axis = this.axis,
|
||||
isDrillable = axis.coll === 'xAxis' && axis.getDDPoints,
|
||||
ddPointsX = isDrillable && axis.getDDPoints(pos);
|
||||
|
||||
if (isDrillable) {
|
||||
if (label && ddPointsX.length) {
|
||||
label.drillable = true;
|
||||
|
||||
|
||||
if (!label.basicStyles) {
|
||||
label.basicStyles = H.merge(label.styles);
|
||||
}
|
||||
|
||||
|
||||
label
|
||||
.addClass('highcharts-drilldown-axis-label')
|
||||
|
||||
.css(axis.chart.options.drilldown.activeAxisLabelStyle)
|
||||
|
||||
.on('click', function(e) {
|
||||
axis.drilldownCategory(pos, e);
|
||||
});
|
||||
|
||||
} else if (label && label.drillable) {
|
||||
|
||||
|
||||
label.styles = {}; // reset for full overwrite of styles
|
||||
label.css(label.basicStyles);
|
||||
|
||||
|
||||
label.on('click', null); // #3806
|
||||
label.removeClass('highcharts-drilldown-axis-label');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Always keep the drillability updated (#3951)
|
||||
*/
|
||||
wrap(Tick.prototype, 'addLabel', function(proceed) {
|
||||
proceed.call(this);
|
||||
this.drillable();
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* On initialization of each point, identify its label and make it clickable. Also, provide a
|
||||
* list of points associated to that label.
|
||||
*/
|
||||
wrap(H.Point.prototype, 'init', function(proceed, series, options, x) {
|
||||
var point = proceed.call(this, series, options, x),
|
||||
xAxis = series.xAxis,
|
||||
tick = xAxis && xAxis.ticks[x];
|
||||
|
||||
if (point.drilldown) {
|
||||
|
||||
// Add the click event to the point
|
||||
H.addEvent(point, 'click', function(e) {
|
||||
if (series.xAxis && series.chart.options.drilldown.allowPointDrilldown === false) {
|
||||
series.xAxis.drilldownCategory(x, e);
|
||||
} else {
|
||||
point.doDrilldown(undefined, undefined, e);
|
||||
}
|
||||
});
|
||||
/*wrap(point, 'importEvents', function (proceed) { // wrapping importEvents makes point.click event work
|
||||
if (!this.hasImportedEvents) {
|
||||
proceed.call(this);
|
||||
H.addEvent(this, 'click', function () {
|
||||
this.doDrilldown();
|
||||
});
|
||||
}
|
||||
});*/
|
||||
|
||||
}
|
||||
|
||||
// Add or remove click handler and style on the tick label
|
||||
if (tick) {
|
||||
tick.drillable();
|
||||
}
|
||||
|
||||
return point;
|
||||
});
|
||||
|
||||
wrap(H.Series.prototype, 'drawDataLabels', function(proceed) {
|
||||
var css = this.chart.options.drilldown.activeDataLabelStyle,
|
||||
renderer = this.chart.renderer;
|
||||
|
||||
proceed.call(this);
|
||||
|
||||
each(this.points, function(point) {
|
||||
var pointCSS = {};
|
||||
if (point.drilldown && point.dataLabel) {
|
||||
if (css.color === 'contrast') {
|
||||
pointCSS.color = renderer.getContrast(point.color || this.color);
|
||||
}
|
||||
point.dataLabel
|
||||
.addClass('highcharts-drilldown-data-label');
|
||||
|
||||
|
||||
point.dataLabel
|
||||
.css(css)
|
||||
.css(pointCSS);
|
||||
|
||||
}
|
||||
}, this);
|
||||
});
|
||||
|
||||
// Mark the trackers with a pointer
|
||||
var type,
|
||||
drawTrackerWrapper = function(proceed) {
|
||||
proceed.call(this);
|
||||
each(this.points, function(point) {
|
||||
if (point.drilldown && point.graphic) {
|
||||
point.graphic.addClass('highcharts-drilldown-point');
|
||||
|
||||
|
||||
point.graphic.css({
|
||||
cursor: 'pointer'
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
for (type in seriesTypes) {
|
||||
if (seriesTypes[type].prototype.supportsDrilldown) {
|
||||
wrap(seriesTypes[type].prototype, 'drawTracker', drawTrackerWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
26
high/highcharts/js/modules/exporting.js
Normal file
26
high/highcharts/js/modules/exporting.js
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
Exporting module
|
||||
|
||||
(c) 2010-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(h){"object"===typeof module&&module.exports?module.exports=h:h(Highcharts)})(function(h){(function(f){var h=f.defaultOptions,q=f.doc,u=f.Chart,v=f.addEvent,D=f.removeEvent,E=f.fireEvent,r=f.createElement,w=f.discardElement,x=f.css,p=f.merge,B=f.pick,k=f.each,t=f.extend,H=f.splat,I=f.isTouchDevice,F=f.win,J=f.Renderer.prototype.symbols;t(h.lang,{printChart:"Print chart",downloadPNG:"Download PNG image",downloadJPEG:"Download JPEG image",downloadPDF:"Download PDF document",downloadSVG:"Download SVG vector image",
|
||||
contextButtonTitle:"Chart context menu"});h.navigation={buttonOptions:{theme:{},symbolSize:14,symbolX:12.5,symbolY:10.5,align:"right",buttonSpacing:3,height:22,verticalAlign:"top",width:24}};p(!0,h.navigation,{menuStyle:{border:"1px solid #999999",background:"#ffffff",padding:"5px 0"},menuItemStyle:{padding:"0.5em 1em",background:"none",color:"#333333",fontSize:I?"14px":"11px",transition:"background 250ms, color 250ms"},menuItemHoverStyle:{background:"#335cad",color:"#ffffff"},buttonOptions:{symbolFill:"#666666",
|
||||
symbolStroke:"#666666",symbolStrokeWidth:3,theme:{fill:"#ffffff",stroke:"none",padding:5}}});h.exporting={type:"image/png",url:"https://export.highcharts.com/",printMaxWidth:780,scale:2,buttons:{contextButton:{className:"highcharts-contextbutton",menuClassName:"highcharts-contextmenu",symbol:"menu",_titleKey:"contextButtonTitle",menuItems:[{textKey:"printChart",onclick:function(){this.print()}},{separator:!0},{textKey:"downloadPNG",onclick:function(){this.exportChart()}},{textKey:"downloadJPEG",onclick:function(){this.exportChart({type:"image/jpeg"})}},
|
||||
{textKey:"downloadPDF",onclick:function(){this.exportChart({type:"application/pdf"})}},{textKey:"downloadSVG",onclick:function(){this.exportChart({type:"image/svg+xml"})}}]}}};f.post=function(a,c,e){var b;a=r("form",p({method:"post",action:a,enctype:"multipart/form-data"},e),{display:"none"},q.body);for(b in c)r("input",{type:"hidden",name:b,value:c[b]},null,a);a.submit();w(a)};t(u.prototype,{sanitizeSVG:function(a){a=a.replace(/zIndex="[^"]+"/g,"").replace(/isShadow="[^"]+"/g,"").replace(/symbolName="[^"]+"/g,
|
||||
"").replace(/jQuery[0-9]+="[^"]+"/g,"").replace(/url\(("|")(\S+)("|")\)/g,"url($2)").replace(/url\([^#]+#/g,"url(#").replace(/<svg /,'<svg xmlns:xlink="http://www.w3.org/1999/xlink" ').replace(/ (NS[0-9]+\:)?href=/g," xlink:href=").replace(/\n/," ").replace(/<\/svg>.*?$/,"</svg>").replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g,'$1="rgb($2)" $1-opacity="$3"').replace(/ /g,"\u00a0").replace(/­/g,"\u00ad");return a=a.replace(/<IMG /g,"<image ").replace(/<(\/?)TITLE>/g,
|
||||
"<$1title>").replace(/height=([^" ]+)/g,'height="$1"').replace(/width=([^" ]+)/g,'width="$1"').replace(/hc-svg-href="([^"]+)">/g,'xlink:href="$1"/>').replace(/ id=([^" >]+)/g,' id="$1"').replace(/class=([^" >]+)/g,'class="$1"').replace(/ transform /g," ").replace(/:(path|rect)/g,"$1").replace(/style="([^"]+)"/g,function(a){return a.toLowerCase()})},getChartHTML:function(){return this.container.innerHTML},getSVG:function(a){var c=this,e,b,g,C,l,d=p(c.options,a),m=d.exporting.allowHTML;q.createElementNS||
|
||||
(q.createElementNS=function(a,b){return q.createElement(b)});b=r("div",null,{position:"absolute",top:"-9999em",width:c.chartWidth+"px",height:c.chartHeight+"px"},q.body);g=c.renderTo.style.width;l=c.renderTo.style.height;g=d.exporting.sourceWidth||d.chart.width||/px$/.test(g)&&parseInt(g,10)||600;l=d.exporting.sourceHeight||d.chart.height||/px$/.test(l)&&parseInt(l,10)||400;t(d.chart,{animation:!1,renderTo:b,forExport:!0,renderer:"SVGRenderer",width:g,height:l});d.exporting.enabled=!1;delete d.data;
|
||||
d.series=[];k(c.series,function(a){C=p(a.userOptions,{animation:!1,enableMouseTracking:!1,showCheckbox:!1,visible:a.visible});C.isInternal||d.series.push(C)});a&&k(["xAxis","yAxis"],function(b){k(H(a[b]),function(a,c){d[b][c]=p(d[b][c],a)})});e=new f.Chart(d,c.callback);k(["xAxis","yAxis"],function(a){k(c[a],function(b,c){var d=e[a][c],g=b.getExtremes(),f=g.userMin,g=g.userMax;!d||void 0===f&&void 0===g||d.setExtremes(f,g,!0,!1)})});g=e.getChartHTML();d=null;e.destroy();w(b);m&&(b=g.match(/<\/svg>(.*?$)/))&&
|
||||
(b='<foreignObject x="0" y="0" width="200" height="200"><body xmlns="http://www.w3.org/1999/xhtml">'+b[1]+"</body></foreignObject>",g=g.replace("</svg>",b+"</svg>"));g=this.sanitizeSVG(g);return g=g.replace(/(url\(#highcharts-[0-9]+)"/g,"$1").replace(/"/g,"'")},getSVGForExport:function(a,c){var e=this.options.exporting;return this.getSVG(p({chart:{borderRadius:0}},e.chartOptions,c,{exporting:{sourceWidth:a&&a.sourceWidth||e.sourceWidth,sourceHeight:a&&a.sourceHeight||e.sourceHeight}}))},
|
||||
exportChart:function(a,c){var e=this.getSVGForExport(a,c);a=p(this.options.exporting,a);f.post(a.url,{filename:a.filename||"chart",type:a.type,width:a.width||0,scale:a.scale,svg:e},a.formAttributes)},print:function(){var a=this,c=a.container,e=[],b=c.parentNode,g=q.body,f=g.childNodes,l=a.options.exporting.printMaxWidth,d,m;if(!a.isPrinting){a.isPrinting=!0;a.pointer.reset(null,0);E(a,"beforePrint");if(m=l&&a.chartWidth>l)d=[a.options.chart.width,void 0,!1],a.setSize(l,void 0,!1);k(f,function(a,b){1===
|
||||
a.nodeType&&(e[b]=a.style.display,a.style.display="none")});g.appendChild(c);F.focus();F.print();setTimeout(function(){b.appendChild(c);k(f,function(a,b){1===a.nodeType&&(a.style.display=e[b])});a.isPrinting=!1;m&&a.setSize.apply(a,d);E(a,"afterPrint")},1E3)}},contextMenu:function(a,c,e,b,g,f,l){var d=this,m=d.options.navigation,h=d.chartWidth,p=d.chartHeight,G="cache-"+a,n=d[G],y=Math.max(g,f),z,A,u,w=function(b){d.pointer.inClass(b.target,a)||A()};n||(d[G]=n=r("div",{className:a},{position:"absolute",
|
||||
zIndex:1E3,padding:y+"px"},d.container),z=r("div",{className:"highcharts-menu"},null,n),x(z,t({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},m.menuStyle)),A=function(){x(n,{display:"none"});l&&l.setState(0);d.openMenu=!1},v(n,"mouseleave",function(){u=setTimeout(A,500)}),v(n,"mouseenter",function(){clearTimeout(u)}),v(q,"mouseup",w),v(d,"destroy",function(){D(q,"mouseup",w)}),k(c,function(a){if(a){var b;a.separator?b=r("hr",null,null,z):(b=r("div",
|
||||
{className:"highcharts-menu-item",onclick:function(b){b&&b.stopPropagation();A();a.onclick&&a.onclick.apply(d,arguments)},innerHTML:a.text||d.options.lang[a.textKey]},null,z),b.onmouseover=function(){x(this,m.menuItemHoverStyle)},b.onmouseout=function(){x(this,m.menuItemStyle)},x(b,t({cursor:"pointer"},m.menuItemStyle)));d.exportDivElements.push(b)}}),d.exportDivElements.push(z,n),d.exportMenuWidth=n.offsetWidth,d.exportMenuHeight=n.offsetHeight);c={display:"block"};e+d.exportMenuWidth>h?c.right=
|
||||
h-e-g-y+"px":c.left=e-y+"px";b+f+d.exportMenuHeight>p&&"top"!==l.alignOptions.verticalAlign?c.bottom=p-b-y+"px":c.top=b+f-y+"px";x(n,c);d.openMenu=!0},addButton:function(a){var c=this,e=c.renderer,b=p(c.options.navigation.buttonOptions,a),g=b.onclick,f=b.menuItems,l,d,m=b.symbolSize||12;c.btnCount||(c.btnCount=0);c.exportDivElements||(c.exportDivElements=[],c.exportSVGElements=[]);if(!1!==b.enabled){var h=b.theme,k=h.states,q=k&&k.hover,k=k&&k.select,n;delete h.states;g?n=function(a){a.stopPropagation();
|
||||
g.call(c,a)}:f&&(n=function(){c.contextMenu(d.menuClassName,f,d.translateX,d.translateY,d.width,d.height,d);d.setState(2)});b.text&&b.symbol?h.paddingLeft=B(h.paddingLeft,25):b.text||t(h,{width:b.width,height:b.height,padding:0});d=e.button(b.text,0,0,n,h,q,k).addClass(a.className).attr({"stroke-linecap":"round",title:c.options.lang[b._titleKey],zIndex:3});d.menuClassName=a.menuClassName||"highcharts-menu-"+c.btnCount++;b.symbol&&(l=e.symbol(b.symbol,b.symbolX-m/2,b.symbolY-m/2,m,m).addClass("highcharts-button-symbol").attr({zIndex:1}).add(d),
|
||||
l.attr({stroke:b.symbolStroke,fill:b.symbolFill,"stroke-width":b.symbolStrokeWidth||1}));d.add().align(t(b,{width:d.width,x:B(b.x,c.buttonOffset)}),!0,"spacingBox");c.buttonOffset+=(d.width+b.buttonSpacing)*("right"===b.align?-1:1);c.exportSVGElements.push(d,l)}},destroyExport:function(a){var c=a?a.target:this;a=c.exportSVGElements;var e=c.exportDivElements;a&&(k(a,function(a,e){a&&(a.onclick=a.ontouchstart=null,c.exportSVGElements[e]=a.destroy())}),a.length=0);e&&(k(e,function(a,e){D(a,"mouseleave");
|
||||
c.exportDivElements[e]=a.onmouseout=a.onmouseover=a.ontouchstart=a.onclick=null;w(a)}),e.length=0)}});J.menu=function(a,c,e,b){return["M",a,c+2.5,"L",a+e,c+2.5,"M",a,c+b/2+.5,"L",a+e,c+b/2+.5,"M",a,c+b-1.5,"L",a+e,c+b-1.5]};u.prototype.renderExporting=function(){var a,c=this.options.exporting,e=c.buttons,b=this.isDirtyExporting||!this.exportSVGElements;this.buttonOffset=0;this.isDirtyExporting&&this.destroyExport();if(b&&!1!==c.enabled){for(a in e)this.addButton(e[a]);this.isDirtyExporting=!1}v(this,
|
||||
"destroy",this.destroyExport)};u.prototype.callbacks.push(function(a){a.renderExporting();v(a,"redraw",a.renderExporting);k(["exporting","navigation"],function(c){a[c]={update:function(e,b){a.isDirtyExporting=!0;p(!0,a.options[c],e);B(b,!0)&&a.redraw()}}})})})(h)});
|
877
high/highcharts/js/modules/exporting.src.js
Normal file
877
high/highcharts/js/modules/exporting.src.js
Normal file
@ -0,0 +1,877 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
* Exporting module
|
||||
*
|
||||
* (c) 2010-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* Exporting module
|
||||
*
|
||||
* (c) 2010-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
|
||||
/* eslint indent:0 */
|
||||
'use strict';
|
||||
|
||||
// create shortcuts
|
||||
var defaultOptions = H.defaultOptions,
|
||||
doc = H.doc,
|
||||
Chart = H.Chart,
|
||||
addEvent = H.addEvent,
|
||||
removeEvent = H.removeEvent,
|
||||
fireEvent = H.fireEvent,
|
||||
createElement = H.createElement,
|
||||
discardElement = H.discardElement,
|
||||
css = H.css,
|
||||
merge = H.merge,
|
||||
pick = H.pick,
|
||||
each = H.each,
|
||||
extend = H.extend,
|
||||
splat = H.splat,
|
||||
isTouchDevice = H.isTouchDevice,
|
||||
win = H.win,
|
||||
SVGRenderer = H.SVGRenderer;
|
||||
|
||||
var symbols = H.Renderer.prototype.symbols;
|
||||
|
||||
// Add language
|
||||
extend(defaultOptions.lang, {
|
||||
printChart: 'Print chart',
|
||||
downloadPNG: 'Download PNG image',
|
||||
downloadJPEG: 'Download JPEG image',
|
||||
downloadPDF: 'Download PDF document',
|
||||
downloadSVG: 'Download SVG vector image',
|
||||
contextButtonTitle: 'Chart context menu'
|
||||
});
|
||||
|
||||
// Buttons and menus are collected in a separate config option set called 'navigation'.
|
||||
// This can be extended later to add control buttons like zoom and pan right click menus.
|
||||
defaultOptions.navigation = {
|
||||
buttonOptions: {
|
||||
theme: {},
|
||||
symbolSize: 14,
|
||||
symbolX: 12.5,
|
||||
symbolY: 10.5,
|
||||
align: 'right',
|
||||
buttonSpacing: 3,
|
||||
height: 22,
|
||||
// text: null,
|
||||
verticalAlign: 'top',
|
||||
width: 24
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Presentational attributes
|
||||
merge(true, defaultOptions.navigation, {
|
||||
menuStyle: {
|
||||
border: '1px solid #999999',
|
||||
background: '#ffffff',
|
||||
padding: '5px 0'
|
||||
},
|
||||
menuItemStyle: {
|
||||
padding: '0.5em 1em',
|
||||
background: 'none',
|
||||
color: '#333333',
|
||||
fontSize: isTouchDevice ? '14px' : '11px',
|
||||
transition: 'background 250ms, color 250ms'
|
||||
},
|
||||
menuItemHoverStyle: {
|
||||
background: '#335cad',
|
||||
color: '#ffffff'
|
||||
},
|
||||
buttonOptions: {
|
||||
symbolFill: '#666666',
|
||||
symbolStroke: '#666666',
|
||||
symbolStrokeWidth: 3,
|
||||
theme: {
|
||||
fill: '#ffffff', // capture hover
|
||||
stroke: 'none',
|
||||
padding: 5
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Add the export related options
|
||||
defaultOptions.exporting = {
|
||||
//enabled: true,
|
||||
//filename: 'chart',
|
||||
type: 'image/png',
|
||||
url: 'https://export.highcharts.com/',
|
||||
//width: undefined,
|
||||
printMaxWidth: 780,
|
||||
scale: 2,
|
||||
buttons: {
|
||||
contextButton: {
|
||||
className: 'highcharts-contextbutton',
|
||||
menuClassName: 'highcharts-contextmenu',
|
||||
//x: -10,
|
||||
symbol: 'menu',
|
||||
_titleKey: 'contextButtonTitle',
|
||||
menuItems: [{
|
||||
textKey: 'printChart',
|
||||
onclick: function() {
|
||||
this.print();
|
||||
}
|
||||
}, {
|
||||
separator: true
|
||||
}, {
|
||||
textKey: 'downloadPNG',
|
||||
onclick: function() {
|
||||
this.exportChart();
|
||||
}
|
||||
}, {
|
||||
textKey: 'downloadJPEG',
|
||||
onclick: function() {
|
||||
this.exportChart({
|
||||
type: 'image/jpeg'
|
||||
});
|
||||
}
|
||||
}, {
|
||||
textKey: 'downloadPDF',
|
||||
onclick: function() {
|
||||
this.exportChart({
|
||||
type: 'application/pdf'
|
||||
});
|
||||
}
|
||||
}, {
|
||||
textKey: 'downloadSVG',
|
||||
onclick: function() {
|
||||
this.exportChart({
|
||||
type: 'image/svg+xml'
|
||||
});
|
||||
}
|
||||
}
|
||||
// Enable this block to add "View SVG" to the dropdown menu
|
||||
/*
|
||||
,{
|
||||
|
||||
text: 'View SVG',
|
||||
onclick: function () {
|
||||
var svg = this.getSVG()
|
||||
.replace(/</g, '\n<')
|
||||
.replace(/>/g, '>');
|
||||
|
||||
doc.body.innerHTML = '<pre>' + svg + '</pre>';
|
||||
}
|
||||
} // */
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add the H.post utility
|
||||
H.post = function(url, data, formAttributes) {
|
||||
var name,
|
||||
form;
|
||||
|
||||
// create the form
|
||||
form = createElement('form', merge({
|
||||
method: 'post',
|
||||
action: url,
|
||||
enctype: 'multipart/form-data'
|
||||
}, formAttributes), {
|
||||
display: 'none'
|
||||
}, doc.body);
|
||||
|
||||
// add the data
|
||||
for (name in data) {
|
||||
createElement('input', {
|
||||
type: 'hidden',
|
||||
name: name,
|
||||
value: data[name]
|
||||
}, null, form);
|
||||
}
|
||||
|
||||
// submit
|
||||
form.submit();
|
||||
|
||||
// clean up
|
||||
discardElement(form);
|
||||
};
|
||||
|
||||
extend(Chart.prototype, {
|
||||
|
||||
/**
|
||||
* A collection of regex fixes on the produces SVG to account for expando properties,
|
||||
* browser bugs, VML problems and other. Returns a cleaned SVG.
|
||||
*/
|
||||
sanitizeSVG: function(svg) {
|
||||
svg = svg
|
||||
.replace(/zIndex="[^"]+"/g, '')
|
||||
.replace(/isShadow="[^"]+"/g, '')
|
||||
.replace(/symbolName="[^"]+"/g, '')
|
||||
.replace(/jQuery[0-9]+="[^"]+"/g, '')
|
||||
.replace(/url\(("|")(\S+)("|")\)/g, 'url($2)')
|
||||
.replace(/url\([^#]+#/g, 'url(#')
|
||||
.replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
|
||||
.replace(/ (NS[0-9]+\:)?href=/g, ' xlink:href=') // #3567
|
||||
.replace(/\n/, ' ')
|
||||
// Any HTML added to the container after the SVG (#894)
|
||||
.replace(/<\/svg>.*?$/, '</svg>')
|
||||
// Batik doesn't support rgba fills and strokes (#3095)
|
||||
.replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g, '$1="rgb($2)" $1-opacity="$3"')
|
||||
/* This fails in IE < 8
|
||||
.replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
|
||||
return s2 +'.'+ s3[0];
|
||||
})*/
|
||||
|
||||
// Replace HTML entities, issue #347
|
||||
.replace(/ /g, '\u00A0') // no-break space
|
||||
.replace(/­/g, '\u00AD'); // soft hyphen
|
||||
|
||||
|
||||
// IE specific
|
||||
svg = svg
|
||||
.replace(/<IMG /g, '<image ')
|
||||
.replace(/<(\/?)TITLE>/g, '<$1title>')
|
||||
.replace(/height=([^" ]+)/g, 'height="$1"')
|
||||
.replace(/width=([^" ]+)/g, 'width="$1"')
|
||||
.replace(/hc-svg-href="([^"]+)">/g, 'xlink:href="$1"/>')
|
||||
.replace(/ id=([^" >]+)/g, ' id="$1"') // #4003
|
||||
.replace(/class=([^" >]+)/g, 'class="$1"')
|
||||
.replace(/ transform /g, ' ')
|
||||
.replace(/:(path|rect)/g, '$1')
|
||||
.replace(/style="([^"]+)"/g, function(s) {
|
||||
return s.toLowerCase();
|
||||
});
|
||||
|
||||
|
||||
return svg;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return innerHTML of chart. Used as hook for plugins.
|
||||
*/
|
||||
getChartHTML: function() {
|
||||
|
||||
return this.container.innerHTML;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return an SVG representation of the chart
|
||||
*
|
||||
* @param additionalOptions {Object} Additional chart options for the generated SVG representation
|
||||
*/
|
||||
getSVG: function(additionalOptions) {
|
||||
var chart = this,
|
||||
chartCopy,
|
||||
sandbox,
|
||||
svg,
|
||||
seriesOptions,
|
||||
sourceWidth,
|
||||
sourceHeight,
|
||||
cssWidth,
|
||||
cssHeight,
|
||||
html,
|
||||
options = merge(chart.options, additionalOptions), // copy the options and add extra options
|
||||
allowHTML = options.exporting.allowHTML;
|
||||
|
||||
|
||||
// IE compatibility hack for generating SVG content that it doesn't really understand
|
||||
if (!doc.createElementNS) {
|
||||
doc.createElementNS = function(ns, tagName) {
|
||||
return doc.createElement(tagName);
|
||||
};
|
||||
}
|
||||
|
||||
// create a sandbox where a new chart will be generated
|
||||
sandbox = createElement('div', null, {
|
||||
position: 'absolute',
|
||||
top: '-9999em',
|
||||
width: chart.chartWidth + 'px',
|
||||
height: chart.chartHeight + 'px'
|
||||
}, doc.body);
|
||||
|
||||
// get the source size
|
||||
cssWidth = chart.renderTo.style.width;
|
||||
cssHeight = chart.renderTo.style.height;
|
||||
sourceWidth = options.exporting.sourceWidth ||
|
||||
options.chart.width ||
|
||||
(/px$/.test(cssWidth) && parseInt(cssWidth, 10)) ||
|
||||
600;
|
||||
sourceHeight = options.exporting.sourceHeight ||
|
||||
options.chart.height ||
|
||||
(/px$/.test(cssHeight) && parseInt(cssHeight, 10)) ||
|
||||
400;
|
||||
|
||||
// override some options
|
||||
extend(options.chart, {
|
||||
animation: false,
|
||||
renderTo: sandbox,
|
||||
forExport: true,
|
||||
renderer: 'SVGRenderer',
|
||||
width: sourceWidth,
|
||||
height: sourceHeight
|
||||
});
|
||||
options.exporting.enabled = false; // hide buttons in print
|
||||
delete options.data; // #3004
|
||||
|
||||
// prepare for replicating the chart
|
||||
options.series = [];
|
||||
each(chart.series, function(serie) {
|
||||
seriesOptions = merge(serie.userOptions, { // #4912
|
||||
animation: false, // turn off animation
|
||||
enableMouseTracking: false,
|
||||
showCheckbox: false,
|
||||
visible: serie.visible
|
||||
});
|
||||
|
||||
if (!seriesOptions.isInternal) { // used for the navigator series that has its own option set
|
||||
options.series.push(seriesOptions);
|
||||
}
|
||||
});
|
||||
|
||||
// Axis options must be merged in one by one, since it may be an array or an object (#2022, #3900)
|
||||
if (additionalOptions) {
|
||||
each(['xAxis', 'yAxis'], function(axisType) {
|
||||
each(splat(additionalOptions[axisType]), function(axisOptions, i) {
|
||||
options[axisType][i] = merge(options[axisType][i], axisOptions);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// generate the chart copy
|
||||
chartCopy = new H.Chart(options, chart.callback);
|
||||
|
||||
// reflect axis extremes in the export
|
||||
each(['xAxis', 'yAxis'], function(axisType) {
|
||||
each(chart[axisType], function(axis, i) {
|
||||
var axisCopy = chartCopy[axisType][i],
|
||||
extremes = axis.getExtremes(),
|
||||
userMin = extremes.userMin,
|
||||
userMax = extremes.userMax;
|
||||
|
||||
if (axisCopy && (userMin !== undefined || userMax !== undefined)) {
|
||||
axisCopy.setExtremes(userMin, userMax, true, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// get the SVG from the container's innerHTML
|
||||
svg = chartCopy.getChartHTML();
|
||||
|
||||
// free up memory
|
||||
options = null;
|
||||
chartCopy.destroy();
|
||||
discardElement(sandbox);
|
||||
|
||||
// Move HTML into a foreignObject
|
||||
if (allowHTML) {
|
||||
html = svg.match(/<\/svg>(.*?$)/);
|
||||
if (html) {
|
||||
html = '<foreignObject x="0" y="0" width="200" height="200">' +
|
||||
'<body xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
html[1] +
|
||||
'</body>' +
|
||||
'</foreignObject>';
|
||||
svg = svg.replace('</svg>', html + '</svg>');
|
||||
}
|
||||
}
|
||||
|
||||
// sanitize
|
||||
svg = this.sanitizeSVG(svg);
|
||||
|
||||
// IE9 beta bugs with innerHTML. Test again with final IE9.
|
||||
svg = svg.replace(/(url\(#highcharts-[0-9]+)"/g, '$1')
|
||||
.replace(/"/g, '\'');
|
||||
|
||||
return svg;
|
||||
},
|
||||
|
||||
getSVGForExport: function(options, chartOptions) {
|
||||
var chartExportingOptions = this.options.exporting;
|
||||
|
||||
return this.getSVG(merge({
|
||||
chart: {
|
||||
borderRadius: 0
|
||||
}
|
||||
},
|
||||
chartExportingOptions.chartOptions,
|
||||
chartOptions, {
|
||||
exporting: {
|
||||
sourceWidth: (options && options.sourceWidth) || chartExportingOptions.sourceWidth,
|
||||
sourceHeight: (options && options.sourceHeight) || chartExportingOptions.sourceHeight
|
||||
}
|
||||
}
|
||||
));
|
||||
},
|
||||
|
||||
/**
|
||||
* Submit the SVG representation of the chart to the server
|
||||
* @param {Object} options Exporting options. Possible members are url, type, width and formAttributes.
|
||||
* @param {Object} chartOptions Additional chart options for the SVG representation of the chart
|
||||
*/
|
||||
exportChart: function(options, chartOptions) {
|
||||
|
||||
var svg = this.getSVGForExport(options, chartOptions);
|
||||
|
||||
// merge the options
|
||||
options = merge(this.options.exporting, options);
|
||||
|
||||
// do the post
|
||||
H.post(options.url, {
|
||||
filename: options.filename || 'chart',
|
||||
type: options.type,
|
||||
width: options.width || 0, // IE8 fails to post undefined correctly, so use 0
|
||||
scale: options.scale,
|
||||
svg: svg
|
||||
}, options.formAttributes);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Print the chart
|
||||
*/
|
||||
print: function() {
|
||||
|
||||
var chart = this,
|
||||
container = chart.container,
|
||||
origDisplay = [],
|
||||
origParent = container.parentNode,
|
||||
body = doc.body,
|
||||
childNodes = body.childNodes,
|
||||
printMaxWidth = chart.options.exporting.printMaxWidth,
|
||||
resetParams,
|
||||
handleMaxWidth;
|
||||
|
||||
if (chart.isPrinting) { // block the button while in printing mode
|
||||
return;
|
||||
}
|
||||
|
||||
chart.isPrinting = true;
|
||||
chart.pointer.reset(null, 0);
|
||||
|
||||
fireEvent(chart, 'beforePrint');
|
||||
|
||||
// Handle printMaxWidth
|
||||
handleMaxWidth = printMaxWidth && chart.chartWidth > printMaxWidth;
|
||||
if (handleMaxWidth) {
|
||||
resetParams = [chart.options.chart.width, undefined, false];
|
||||
chart.setSize(printMaxWidth, undefined, false);
|
||||
}
|
||||
|
||||
// hide all body content
|
||||
each(childNodes, function(node, i) {
|
||||
if (node.nodeType === 1) {
|
||||
origDisplay[i] = node.style.display;
|
||||
node.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// pull out the chart
|
||||
body.appendChild(container);
|
||||
|
||||
// print
|
||||
win.focus(); // #1510
|
||||
win.print();
|
||||
|
||||
// allow the browser to prepare before reverting
|
||||
setTimeout(function() {
|
||||
|
||||
// put the chart back in
|
||||
origParent.appendChild(container);
|
||||
|
||||
// restore all body content
|
||||
each(childNodes, function(node, i) {
|
||||
if (node.nodeType === 1) {
|
||||
node.style.display = origDisplay[i];
|
||||
}
|
||||
});
|
||||
|
||||
chart.isPrinting = false;
|
||||
|
||||
// Reset printMaxWidth
|
||||
if (handleMaxWidth) {
|
||||
chart.setSize.apply(chart, resetParams);
|
||||
}
|
||||
|
||||
fireEvent(chart, 'afterPrint');
|
||||
|
||||
}, 1000);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Display a popup menu for choosing the export type
|
||||
*
|
||||
* @param {String} className An identifier for the menu
|
||||
* @param {Array} items A collection with text and onclicks for the items
|
||||
* @param {Number} x The x position of the opener button
|
||||
* @param {Number} y The y position of the opener button
|
||||
* @param {Number} width The width of the opener button
|
||||
* @param {Number} height The height of the opener button
|
||||
*/
|
||||
contextMenu: function(className, items, x, y, width, height, button) {
|
||||
var chart = this,
|
||||
navOptions = chart.options.navigation,
|
||||
chartWidth = chart.chartWidth,
|
||||
chartHeight = chart.chartHeight,
|
||||
cacheName = 'cache-' + className,
|
||||
menu = chart[cacheName],
|
||||
menuPadding = Math.max(width, height), // for mouse leave detection
|
||||
innerMenu,
|
||||
hide,
|
||||
hideTimer,
|
||||
menuStyle,
|
||||
docMouseUpHandler = function(e) {
|
||||
if (!chart.pointer.inClass(e.target, className)) {
|
||||
hide();
|
||||
}
|
||||
};
|
||||
|
||||
// create the menu only the first time
|
||||
if (!menu) {
|
||||
|
||||
// create a HTML element above the SVG
|
||||
chart[cacheName] = menu = createElement('div', {
|
||||
className: className
|
||||
}, {
|
||||
position: 'absolute',
|
||||
zIndex: 1000,
|
||||
padding: menuPadding + 'px'
|
||||
}, chart.container);
|
||||
|
||||
innerMenu = createElement('div', {
|
||||
className: 'highcharts-menu'
|
||||
}, null, menu);
|
||||
|
||||
|
||||
// Presentational CSS
|
||||
css(innerMenu, extend({
|
||||
MozBoxShadow: '3px 3px 10px #888',
|
||||
WebkitBoxShadow: '3px 3px 10px #888',
|
||||
boxShadow: '3px 3px 10px #888'
|
||||
}, navOptions.menuStyle));
|
||||
|
||||
|
||||
// hide on mouse out
|
||||
hide = function() {
|
||||
css(menu, {
|
||||
display: 'none'
|
||||
});
|
||||
if (button) {
|
||||
button.setState(0);
|
||||
}
|
||||
chart.openMenu = false;
|
||||
};
|
||||
|
||||
// Hide the menu some time after mouse leave (#1357)
|
||||
addEvent(menu, 'mouseleave', function() {
|
||||
hideTimer = setTimeout(hide, 500);
|
||||
});
|
||||
addEvent(menu, 'mouseenter', function() {
|
||||
clearTimeout(hideTimer);
|
||||
});
|
||||
|
||||
|
||||
// Hide it on clicking or touching outside the menu (#2258, #2335, #2407)
|
||||
addEvent(doc, 'mouseup', docMouseUpHandler);
|
||||
addEvent(chart, 'destroy', function() {
|
||||
removeEvent(doc, 'mouseup', docMouseUpHandler);
|
||||
});
|
||||
|
||||
|
||||
// create the items
|
||||
each(items, function(item) {
|
||||
if (item) {
|
||||
var element;
|
||||
|
||||
if (item.separator) {
|
||||
element = createElement('hr', null, null, innerMenu);
|
||||
|
||||
} else {
|
||||
element = createElement('div', {
|
||||
className: 'highcharts-menu-item',
|
||||
onclick: function(e) {
|
||||
if (e) { // IE7
|
||||
e.stopPropagation();
|
||||
}
|
||||
hide();
|
||||
if (item.onclick) {
|
||||
item.onclick.apply(chart, arguments);
|
||||
}
|
||||
},
|
||||
innerHTML: item.text || chart.options.lang[item.textKey]
|
||||
}, null, innerMenu);
|
||||
|
||||
|
||||
element.onmouseover = function() {
|
||||
css(this, navOptions.menuItemHoverStyle);
|
||||
};
|
||||
element.onmouseout = function() {
|
||||
css(this, navOptions.menuItemStyle);
|
||||
};
|
||||
css(element, extend({
|
||||
cursor: 'pointer'
|
||||
}, navOptions.menuItemStyle));
|
||||
|
||||
}
|
||||
|
||||
// Keep references to menu divs to be able to destroy them
|
||||
chart.exportDivElements.push(element);
|
||||
}
|
||||
});
|
||||
|
||||
// Keep references to menu and innerMenu div to be able to destroy them
|
||||
chart.exportDivElements.push(innerMenu, menu);
|
||||
|
||||
chart.exportMenuWidth = menu.offsetWidth;
|
||||
chart.exportMenuHeight = menu.offsetHeight;
|
||||
}
|
||||
|
||||
menuStyle = {
|
||||
display: 'block'
|
||||
};
|
||||
|
||||
// if outside right, right align it
|
||||
if (x + chart.exportMenuWidth > chartWidth) {
|
||||
menuStyle.right = (chartWidth - x - width - menuPadding) + 'px';
|
||||
} else {
|
||||
menuStyle.left = (x - menuPadding) + 'px';
|
||||
}
|
||||
// if outside bottom, bottom align it
|
||||
if (y + height + chart.exportMenuHeight > chartHeight && button.alignOptions.verticalAlign !== 'top') {
|
||||
menuStyle.bottom = (chartHeight - y - menuPadding) + 'px';
|
||||
} else {
|
||||
menuStyle.top = (y + height - menuPadding) + 'px';
|
||||
}
|
||||
|
||||
css(menu, menuStyle);
|
||||
chart.openMenu = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the export button to the chart
|
||||
*/
|
||||
addButton: function(options) {
|
||||
var chart = this,
|
||||
renderer = chart.renderer,
|
||||
btnOptions = merge(chart.options.navigation.buttonOptions, options),
|
||||
onclick = btnOptions.onclick,
|
||||
menuItems = btnOptions.menuItems,
|
||||
symbol,
|
||||
button,
|
||||
symbolSize = btnOptions.symbolSize || 12;
|
||||
if (!chart.btnCount) {
|
||||
chart.btnCount = 0;
|
||||
}
|
||||
|
||||
// Keeps references to the button elements
|
||||
if (!chart.exportDivElements) {
|
||||
chart.exportDivElements = [];
|
||||
chart.exportSVGElements = [];
|
||||
}
|
||||
|
||||
if (btnOptions.enabled === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var attr = btnOptions.theme,
|
||||
states = attr.states,
|
||||
hover = states && states.hover,
|
||||
select = states && states.select,
|
||||
callback;
|
||||
|
||||
delete attr.states;
|
||||
|
||||
if (onclick) {
|
||||
callback = function(e) {
|
||||
e.stopPropagation();
|
||||
onclick.call(chart, e);
|
||||
};
|
||||
|
||||
} else if (menuItems) {
|
||||
callback = function() {
|
||||
chart.contextMenu(
|
||||
button.menuClassName,
|
||||
menuItems,
|
||||
button.translateX,
|
||||
button.translateY,
|
||||
button.width,
|
||||
button.height,
|
||||
button
|
||||
);
|
||||
button.setState(2);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (btnOptions.text && btnOptions.symbol) {
|
||||
attr.paddingLeft = pick(attr.paddingLeft, 25);
|
||||
|
||||
} else if (!btnOptions.text) {
|
||||
extend(attr, {
|
||||
width: btnOptions.width,
|
||||
height: btnOptions.height,
|
||||
padding: 0
|
||||
});
|
||||
}
|
||||
|
||||
button = renderer.button(btnOptions.text, 0, 0, callback, attr, hover, select)
|
||||
.addClass(options.className)
|
||||
.attr({
|
||||
|
||||
'stroke-linecap': 'round',
|
||||
|
||||
title: chart.options.lang[btnOptions._titleKey],
|
||||
zIndex: 3 // #4955
|
||||
});
|
||||
button.menuClassName = options.menuClassName || 'highcharts-menu-' + chart.btnCount++;
|
||||
|
||||
if (btnOptions.symbol) {
|
||||
symbol = renderer.symbol(
|
||||
btnOptions.symbol,
|
||||
btnOptions.symbolX - (symbolSize / 2),
|
||||
btnOptions.symbolY - (symbolSize / 2),
|
||||
symbolSize,
|
||||
symbolSize
|
||||
)
|
||||
.addClass('highcharts-button-symbol')
|
||||
.attr({
|
||||
zIndex: 1
|
||||
}).add(button);
|
||||
|
||||
|
||||
symbol.attr({
|
||||
stroke: btnOptions.symbolStroke,
|
||||
fill: btnOptions.symbolFill,
|
||||
'stroke-width': btnOptions.symbolStrokeWidth || 1
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
button.add()
|
||||
.align(extend(btnOptions, {
|
||||
width: button.width,
|
||||
x: pick(btnOptions.x, chart.buttonOffset) // #1654
|
||||
}), true, 'spacingBox');
|
||||
|
||||
chart.buttonOffset += (button.width + btnOptions.buttonSpacing) * (btnOptions.align === 'right' ? -1 : 1);
|
||||
|
||||
chart.exportSVGElements.push(button, symbol);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the buttons.
|
||||
*/
|
||||
destroyExport: function(e) {
|
||||
var chart = e ? e.target : this,
|
||||
exportSVGElements = chart.exportSVGElements,
|
||||
exportDivElements = chart.exportDivElements;
|
||||
|
||||
// Destroy the extra buttons added
|
||||
if (exportSVGElements) {
|
||||
each(exportSVGElements, function(elem, i) {
|
||||
|
||||
// Destroy and null the svg/vml elements
|
||||
if (elem) { // #1822
|
||||
elem.onclick = elem.ontouchstart = null;
|
||||
chart.exportSVGElements[i] = elem.destroy();
|
||||
}
|
||||
});
|
||||
exportSVGElements.length = 0;
|
||||
}
|
||||
|
||||
// Destroy the divs for the menu
|
||||
if (exportDivElements) {
|
||||
each(exportDivElements, function(elem, i) {
|
||||
|
||||
// Remove the event handler
|
||||
removeEvent(elem, 'mouseleave');
|
||||
|
||||
// Remove inline events
|
||||
chart.exportDivElements[i] = elem.onmouseout = elem.onmouseover = elem.ontouchstart = elem.onclick = null;
|
||||
|
||||
// Destroy the div by moving to garbage bin
|
||||
discardElement(elem);
|
||||
});
|
||||
exportDivElements.length = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
symbols.menu = function(x, y, width, height) {
|
||||
var arr = [
|
||||
'M', x, y + 2.5,
|
||||
'L', x + width, y + 2.5,
|
||||
'M', x, y + height / 2 + 0.5,
|
||||
'L', x + width, y + height / 2 + 0.5,
|
||||
'M', x, y + height - 1.5,
|
||||
'L', x + width, y + height - 1.5
|
||||
];
|
||||
return arr;
|
||||
};
|
||||
|
||||
// Add the buttons on chart load
|
||||
Chart.prototype.renderExporting = function() {
|
||||
var n,
|
||||
exportingOptions = this.options.exporting,
|
||||
buttons = exportingOptions.buttons,
|
||||
isDirty = this.isDirtyExporting || !this.exportSVGElements;
|
||||
|
||||
this.buttonOffset = 0;
|
||||
if (this.isDirtyExporting) {
|
||||
this.destroyExport();
|
||||
}
|
||||
|
||||
if (isDirty && exportingOptions.enabled !== false) {
|
||||
|
||||
for (n in buttons) {
|
||||
this.addButton(buttons[n]);
|
||||
}
|
||||
|
||||
this.isDirtyExporting = false;
|
||||
}
|
||||
|
||||
// Destroy the export elements at chart destroy
|
||||
addEvent(this, 'destroy', this.destroyExport);
|
||||
};
|
||||
|
||||
Chart.prototype.callbacks.push(function(chart) {
|
||||
|
||||
function update(prop, options, redraw) {
|
||||
chart.isDirtyExporting = true;
|
||||
merge(true, chart.options[prop], options);
|
||||
if (pick(redraw, true)) {
|
||||
chart.redraw();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
chart.renderExporting();
|
||||
|
||||
addEvent(chart, 'redraw', chart.renderExporting);
|
||||
|
||||
// Add update methods to handle chart.update and chart.exporting.update
|
||||
// and chart.navigation.update.
|
||||
each(['exporting', 'navigation'], function(prop) {
|
||||
chart[prop] = {
|
||||
update: function(options, redraw) {
|
||||
update(prop, options, redraw);
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
12
high/highcharts/js/modules/funnel.js
Normal file
12
high/highcharts/js/modules/funnel.js
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
Highcharts funnel module
|
||||
|
||||
(c) 2010-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(c){"object"===typeof module&&module.exports?module.exports=c:c(Highcharts)})(function(c){(function(c){var n=c.seriesType,z=c.seriesTypes,F=c.noop,G=c.each;n("funnel","pie",{animation:!1,center:["50%","50%"],width:"90%",neckWidth:"30%",height:"100%",neckHeight:"25%",reversed:!1,size:!0,dataLabels:{connectorWidth:1},states:{select:{color:"#cccccc",borderColor:"#000000",shadow:!1}}},{animate:F,translate:function(){var b=function(a,b){return/%$/.test(a)?b*parseInt(a,10)/100:parseInt(a,10)},
|
||||
c=0,e=this.chart,d=this.options,r=d.reversed,H=d.ignoreHiddenPoint,t=e.plotWidth,e=e.plotHeight,p=0,n=d.center,f=b(n[0],t),q=b(n[1],e),z=b(d.width,t),h,v,k=b(d.height,e),w=b(d.neckWidth,t),D=b(d.neckHeight,e),x=q-k/2+k-D,b=this.data,A,B,I="left"===d.dataLabels.position?1:0,C,l,E,u,g,y,m;this.getWidthAt=v=function(a){var b=q-k/2;return a>x||k===D?w:w+(z-w)*(1-(a-b)/(k-D))};this.getX=function(a,b){return f+(b?-1:1)*(v(r?2*q-a:a)/2+d.dataLabels.distance)};this.center=[f,q,k];this.centerX=f;G(b,function(a){H&&
|
||||
!1===a.visible||(c+=a.y)});G(b,function(a){m=null;B=c?a.y/c:0;l=q-k/2+p*k;g=l+B*k;h=v(l);C=f-h/2;E=C+h;h=v(g);u=f-h/2;y=u+h;l>x?(C=u=f-w/2,E=y=f+w/2):g>x&&(m=g,h=v(x),u=f-h/2,y=u+h,g=x);r&&(l=2*q-l,g=2*q-g,m=m?2*q-m:null);A=["M",C,l,"L",E,l,y,g];m&&A.push(y,m,u,m);A.push(u,g,"Z");a.shapeType="path";a.shapeArgs={d:A};a.percentage=100*B;a.plotX=f;a.plotY=(l+(m||g))/2;a.tooltipPos=[f,a.plotY];a.slice=F;a.half=I;H&&!1===a.visible||(p+=B)})},drawPoints:z.column.prototype.drawPoints,sortByAngle:function(b){b.sort(function(b,
|
||||
c){return b.plotY-c.plotY})},drawDataLabels:function(){var b=this.data,c=this.options.dataLabels.distance,e,d,r,n=b.length,t,p;for(this.center[2]-=2*c;n--;)r=b[n],d=(e=r.half)?1:-1,p=r.plotY,t=this.getX(p,e),r.labelPos=[0,p,t+(c-5)*d,p,t+c*d,p,e?"right":"left",0];z.pie.prototype.drawDataLabels.call(this)}});n("pyramid","funnel",{neckWidth:"0%",neckHeight:"0%",reversed:!0})})(c)});
|
304
high/highcharts/js/modules/funnel.src.js
Normal file
304
high/highcharts/js/modules/funnel.src.js
Normal file
@ -0,0 +1,304 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
* Highcharts funnel module
|
||||
*
|
||||
* (c) 2010-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(Highcharts) {
|
||||
/**
|
||||
* Highcharts funnel module
|
||||
*
|
||||
* (c) 2010-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
/* eslint indent:0 */
|
||||
'use strict';
|
||||
|
||||
// create shortcuts
|
||||
var seriesType = Highcharts.seriesType,
|
||||
seriesTypes = Highcharts.seriesTypes,
|
||||
noop = Highcharts.noop,
|
||||
each = Highcharts.each;
|
||||
|
||||
|
||||
seriesType('funnel', 'pie', {
|
||||
animation: false,
|
||||
center: ['50%', '50%'],
|
||||
width: '90%',
|
||||
neckWidth: '30%',
|
||||
height: '100%',
|
||||
neckHeight: '25%',
|
||||
reversed: false,
|
||||
size: true, // to avoid adapting to data label size in Pie.drawDataLabels
|
||||
|
||||
|
||||
// Presentational
|
||||
dataLabels: {
|
||||
//position: 'right',
|
||||
connectorWidth: 1
|
||||
//connectorColor: null
|
||||
},
|
||||
states: {
|
||||
select: {
|
||||
color: '#cccccc',
|
||||
borderColor: '#000000',
|
||||
shadow: false
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// Properties
|
||||
{
|
||||
animate: noop,
|
||||
|
||||
/**
|
||||
* Overrides the pie translate method
|
||||
*/
|
||||
translate: function() {
|
||||
|
||||
var
|
||||
// Get positions - either an integer or a percentage string must be given
|
||||
getLength = function(length, relativeTo) {
|
||||
return (/%$/).test(length) ?
|
||||
relativeTo * parseInt(length, 10) / 100 :
|
||||
parseInt(length, 10);
|
||||
},
|
||||
|
||||
sum = 0,
|
||||
series = this,
|
||||
chart = series.chart,
|
||||
options = series.options,
|
||||
reversed = options.reversed,
|
||||
ignoreHiddenPoint = options.ignoreHiddenPoint,
|
||||
plotWidth = chart.plotWidth,
|
||||
plotHeight = chart.plotHeight,
|
||||
cumulative = 0, // start at top
|
||||
center = options.center,
|
||||
centerX = getLength(center[0], plotWidth),
|
||||
centerY = getLength(center[1], plotHeight),
|
||||
width = getLength(options.width, plotWidth),
|
||||
tempWidth,
|
||||
getWidthAt,
|
||||
height = getLength(options.height, plotHeight),
|
||||
neckWidth = getLength(options.neckWidth, plotWidth),
|
||||
neckHeight = getLength(options.neckHeight, plotHeight),
|
||||
neckY = (centerY - height / 2) + height - neckHeight,
|
||||
data = series.data,
|
||||
path,
|
||||
fraction,
|
||||
half = options.dataLabels.position === 'left' ? 1 : 0,
|
||||
|
||||
x1,
|
||||
y1,
|
||||
x2,
|
||||
x3,
|
||||
y3,
|
||||
x4,
|
||||
y5;
|
||||
|
||||
// Return the width at a specific y coordinate
|
||||
series.getWidthAt = getWidthAt = function(y) {
|
||||
var top = (centerY - height / 2);
|
||||
|
||||
return y > neckY || height === neckHeight ?
|
||||
neckWidth :
|
||||
neckWidth + (width - neckWidth) * (1 - (y - top) / (height - neckHeight));
|
||||
};
|
||||
series.getX = function(y, half) {
|
||||
return centerX + (half ? -1 : 1) * ((getWidthAt(reversed ? 2 * centerY - y : y) / 2) + options.dataLabels.distance);
|
||||
};
|
||||
|
||||
// Expose
|
||||
series.center = [centerX, centerY, height];
|
||||
series.centerX = centerX;
|
||||
|
||||
/*
|
||||
* Individual point coordinate naming:
|
||||
*
|
||||
* x1,y1 _________________ x2,y1
|
||||
* \ /
|
||||
* \ /
|
||||
* \ /
|
||||
* \ /
|
||||
* \ /
|
||||
* x3,y3 _________ x4,y3
|
||||
*
|
||||
* Additional for the base of the neck:
|
||||
*
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* x3,y5 _________ x4,y5
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
// get the total sum
|
||||
each(data, function(point) {
|
||||
if (!ignoreHiddenPoint || point.visible !== false) {
|
||||
sum += point.y;
|
||||
}
|
||||
});
|
||||
|
||||
each(data, function(point) {
|
||||
// set start and end positions
|
||||
y5 = null;
|
||||
fraction = sum ? point.y / sum : 0;
|
||||
y1 = centerY - height / 2 + cumulative * height;
|
||||
y3 = y1 + fraction * height;
|
||||
//tempWidth = neckWidth + (width - neckWidth) * ((height - neckHeight - y1) / (height - neckHeight));
|
||||
tempWidth = getWidthAt(y1);
|
||||
x1 = centerX - tempWidth / 2;
|
||||
x2 = x1 + tempWidth;
|
||||
tempWidth = getWidthAt(y3);
|
||||
x3 = centerX - tempWidth / 2;
|
||||
x4 = x3 + tempWidth;
|
||||
|
||||
// the entire point is within the neck
|
||||
if (y1 > neckY) {
|
||||
x1 = x3 = centerX - neckWidth / 2;
|
||||
x2 = x4 = centerX + neckWidth / 2;
|
||||
|
||||
// the base of the neck
|
||||
} else if (y3 > neckY) {
|
||||
y5 = y3;
|
||||
|
||||
tempWidth = getWidthAt(neckY);
|
||||
x3 = centerX - tempWidth / 2;
|
||||
x4 = x3 + tempWidth;
|
||||
|
||||
y3 = neckY;
|
||||
}
|
||||
|
||||
if (reversed) {
|
||||
y1 = 2 * centerY - y1;
|
||||
y3 = 2 * centerY - y3;
|
||||
y5 = (y5 ? 2 * centerY - y5 : null);
|
||||
}
|
||||
// save the path
|
||||
path = [
|
||||
'M',
|
||||
x1, y1,
|
||||
'L',
|
||||
x2, y1,
|
||||
x4, y3
|
||||
];
|
||||
if (y5) {
|
||||
path.push(x4, y5, x3, y5);
|
||||
}
|
||||
path.push(x3, y3, 'Z');
|
||||
|
||||
// prepare for using shared dr
|
||||
point.shapeType = 'path';
|
||||
point.shapeArgs = {
|
||||
d: path
|
||||
};
|
||||
|
||||
|
||||
// for tooltips and data labels
|
||||
point.percentage = fraction * 100;
|
||||
point.plotX = centerX;
|
||||
point.plotY = (y1 + (y5 || y3)) / 2;
|
||||
|
||||
// Placement of tooltips and data labels
|
||||
point.tooltipPos = [
|
||||
centerX,
|
||||
point.plotY
|
||||
];
|
||||
|
||||
// Slice is a noop on funnel points
|
||||
point.slice = noop;
|
||||
|
||||
// Mimicking pie data label placement logic
|
||||
point.half = half;
|
||||
|
||||
if (!ignoreHiddenPoint || point.visible !== false) {
|
||||
cumulative += fraction;
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Draw a single point (wedge)
|
||||
* @param {Object} point The point object
|
||||
* @param {Object} color The color of the point
|
||||
* @param {Number} brightness The brightness relative to the color
|
||||
*/
|
||||
drawPoints: seriesTypes.column.prototype.drawPoints,
|
||||
|
||||
/**
|
||||
* Funnel items don't have angles (#2289)
|
||||
*/
|
||||
sortByAngle: function(points) {
|
||||
points.sort(function(a, b) {
|
||||
return a.plotY - b.plotY;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Extend the pie data label method
|
||||
*/
|
||||
drawDataLabels: function() {
|
||||
var data = this.data,
|
||||
labelDistance = this.options.dataLabels.distance,
|
||||
leftSide,
|
||||
sign,
|
||||
point,
|
||||
i = data.length,
|
||||
x,
|
||||
y;
|
||||
|
||||
// In the original pie label anticollision logic, the slots are distributed
|
||||
// from one labelDistance above to one labelDistance below the pie. In funnels
|
||||
// we don't want this.
|
||||
this.center[2] -= 2 * labelDistance;
|
||||
|
||||
// Set the label position array for each point.
|
||||
while (i--) {
|
||||
point = data[i];
|
||||
leftSide = point.half;
|
||||
sign = leftSide ? 1 : -1;
|
||||
y = point.plotY;
|
||||
x = this.getX(y, leftSide);
|
||||
|
||||
// set the anchor point for data labels
|
||||
point.labelPos = [
|
||||
0, // first break of connector
|
||||
y, // a/a
|
||||
x + (labelDistance - 5) * sign, // second break, right outside point shape
|
||||
y, // a/a
|
||||
x + labelDistance * sign, // landing point for connector
|
||||
y, // a/a
|
||||
leftSide ? 'right' : 'left', // alignment
|
||||
0 // center angle
|
||||
];
|
||||
}
|
||||
|
||||
seriesTypes.pie.prototype.drawDataLabels.call(this);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Pyramid series type.
|
||||
* A pyramid series is a special type of funnel, without neck and reversed by default.
|
||||
*/
|
||||
seriesType('pyramid', 'funnel', {
|
||||
neckWidth: '0%',
|
||||
neckHeight: '0%',
|
||||
reversed: true
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
24
high/highcharts/js/modules/heatmap.js
Normal file
24
high/highcharts/js/modules/heatmap.js
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
|
||||
(c) 2009-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(p){"object"===typeof module&&module.exports?module.exports=p:p(Highcharts)})(function(p){(function(b){var h=b.Axis,t=b.Chart,l=b.color,k,n=b.each,w=b.extend,x=b.isNumber,q=b.Legend,m=b.LegendSymbolMixin,f=b.noop,r=b.merge,v=b.pick,u=b.wrap;k=b.ColorAxis=function(){this.init.apply(this,arguments)};w(k.prototype,h.prototype);w(k.prototype,{defaultColorAxisOptions:{lineWidth:0,minPadding:0,maxPadding:0,gridLineWidth:1,tickPixelInterval:72,startOnTick:!0,endOnTick:!0,offset:0,marker:{animation:{duration:50},
|
||||
width:.01,color:"#999999"},labels:{overflow:"justify"},minColor:"#e6ebf5",maxColor:"#003399",tickLength:5,showInLegend:!0},init:function(a,c){var d="vertical"!==a.options.legend.layout,e;this.coll="colorAxis";e=r(this.defaultColorAxisOptions,{side:d?2:1,reversed:!d},c,{opposite:!d,showEmpty:!1,title:null});h.prototype.init.call(this,a,e);c.dataClasses&&this.initDataClasses(c);this.initStops(c);this.horiz=d;this.zoomEnabled=!1;this.defaultLegendLength=200},tweenColors:function(a,c,d){var e;c.rgba.length&&
|
||||
a.rgba.length?(a=a.rgba,c=c.rgba,e=1!==c[3]||1!==a[3],a=(e?"rgba(":"rgb(")+Math.round(c[0]+(a[0]-c[0])*(1-d))+","+Math.round(c[1]+(a[1]-c[1])*(1-d))+","+Math.round(c[2]+(a[2]-c[2])*(1-d))+(e?","+(c[3]+(a[3]-c[3])*(1-d)):"")+")"):a=c.input||"none";return a},initDataClasses:function(a){var c=this,d=this.chart,e,g=0,b=d.options.chart.colorCount,y=this.options,m=a.dataClasses.length;this.dataClasses=e=[];this.legendItems=[];n(a.dataClasses,function(a,n){var f;a=r(a);e.push(a);a.color||("category"===y.dataClassColor?
|
||||
(f=d.options.colors,b=f.length,a.color=f[g],a.colorIndex=g,g++,g===b&&(g=0)):a.color=c.tweenColors(l(y.minColor),l(y.maxColor),2>m?.5:n/(m-1)))})},initStops:function(a){this.stops=a.stops||[[0,this.options.minColor],[1,this.options.maxColor]];n(this.stops,function(a){a.color=l(a[1])})},setOptions:function(a){h.prototype.setOptions.call(this,a);this.options.crosshair=this.options.marker},setAxisSize:function(){var a=this.legendSymbol,c=this.chart,d=c.options.legend||{},e,g;a?(this.left=d=a.attr("x"),
|
||||
this.top=e=a.attr("y"),this.width=g=a.attr("width"),this.height=a=a.attr("height"),this.right=c.chartWidth-d-g,this.bottom=c.chartHeight-e-a,this.len=this.horiz?g:a,this.pos=this.horiz?d:e):this.len=(this.horiz?d.symbolWidth:d.symbolHeight)||this.defaultLegendLength},toColor:function(a,c){var d,e=this.stops,g,b=this.dataClasses,m,f;if(b)for(f=b.length;f--;){if(m=b[f],g=m.from,e=m.to,(void 0===g||a>=g)&&(void 0===e||a<=e)){d=m.color;c&&(c.dataClass=f,c.colorIndex=m.colorIndex);break}}else{this.isLog&&
|
||||
(a=this.val2lin(a));d=1-(this.max-a)/(this.max-this.min||1);for(f=e.length;f--&&!(d>e[f][0]););g=e[f]||e[f+1];e=e[f+1]||g;d=1-(e[0]-d)/(e[0]-g[0]||1);d=this.tweenColors(g.color,e.color,d)}return d},getOffset:function(){var a=this.legendGroup,c=this.chart.axisOffset[this.side];a&&(this.axisParent=a,h.prototype.getOffset.call(this),this.added||(this.added=!0,this.labelLeft=0,this.labelRight=this.width),this.chart.axisOffset[this.side]=c)},setLegendColor:function(){var a,c=this.options,d=this.reversed;
|
||||
a=d?1:0;d=d?0:1;a=this.horiz?[a,0,d,0]:[0,d,0,a];this.legendColor={linearGradient:{x1:a[0],y1:a[1],x2:a[2],y2:a[3]},stops:c.stops||[[0,c.minColor],[1,c.maxColor]]}},drawLegendSymbol:function(a,c){var d=a.padding,e=a.options,g=this.horiz,b=v(e.symbolWidth,g?this.defaultLegendLength:12),f=v(e.symbolHeight,g?12:this.defaultLegendLength),m=v(e.labelPadding,g?16:30),e=v(e.itemDistance,10);this.setLegendColor();c.legendSymbol=this.chart.renderer.rect(0,a.baseline-11,b,f).attr({zIndex:1}).add(c.legendGroup);
|
||||
this.legendItemWidth=b+d+(g?e:m);this.legendItemHeight=f+d+(g?m:0)},setState:f,visible:!0,setVisible:f,getSeriesExtremes:function(){var a;this.series.length&&(a=this.series[0],this.dataMin=a.valueMin,this.dataMax=a.valueMax)},drawCrosshair:function(a,c){var d=c&&c.plotX,e=c&&c.plotY,g,b=this.pos,f=this.len;c&&(g=this.toPixels(c[c.series.colorKey]),g<b?g=b-2:g>b+f&&(g=b+f+2),c.plotX=g,c.plotY=this.len-g,h.prototype.drawCrosshair.call(this,a,c),c.plotX=d,c.plotY=e,this.cross&&(this.cross.addClass("highcharts-coloraxis-marker").add(this.legendGroup),
|
||||
this.cross.attr({fill:this.crosshair.color})))},getPlotLinePath:function(a,c,d,e,b){return x(b)?this.horiz?["M",b-4,this.top-6,"L",b+4,this.top-6,b,this.top,"Z"]:["M",this.left,b,"L",this.left-6,b+6,this.left-6,b-6,"Z"]:h.prototype.getPlotLinePath.call(this,a,c,d,e)},update:function(a,c){var d=this.chart,b=d.legend;n(this.series,function(a){a.isDirtyData=!0});a.dataClasses&&b.allItems&&(n(b.allItems,function(a){a.isDataClass&&a.legendGroup.destroy()}),d.isDirtyLegend=!0);d.options[this.coll]=r(this.userOptions,
|
||||
a);h.prototype.update.call(this,a,c);this.legendItem&&(this.setLegendColor(),b.colorizeItem(this,!0))},getDataClassLegendSymbols:function(){var a=this,c=this.chart,d=this.legendItems,e=c.options.legend,g=e.valueDecimals,u=e.valueSuffix||"",h;d.length||n(this.dataClasses,function(e,q){var k=!0,r=e.from,l=e.to;h="";void 0===r?h="< ":void 0===l&&(h="> ");void 0!==r&&(h+=b.numberFormat(r,g)+u);void 0!==r&&void 0!==l&&(h+=" - ");void 0!==l&&(h+=b.numberFormat(l,g)+u);d.push(w({chart:c,name:h,options:{},
|
||||
drawLegendSymbol:m.drawRectangle,visible:!0,setState:f,isDataClass:!0,setVisible:function(){k=this.visible=!k;n(a.series,function(a){n(a.points,function(a){a.dataClass===q&&a.setVisible(k)})});c.legend.colorizeItem(this,k)}},e))});return d},name:""});n(["fill","stroke"],function(a){b.Fx.prototype[a+"Setter"]=function(){this.elem.attr(a,k.prototype.tweenColors(l(this.start),l(this.end),this.pos))}});u(t.prototype,"getAxes",function(a){var c=this.options.colorAxis;a.call(this);this.colorAxis=[];c&&
|
||||
new k(this,c)});u(q.prototype,"getAllItems",function(a){var c=[],d=this.chart.colorAxis[0];d&&d.options&&(d.options.showInLegend&&(d.options.dataClasses?c=c.concat(d.getDataClassLegendSymbols()):c.push(d)),n(d.series,function(a){a.options.showInLegend=!1}));return c.concat(a.call(this))});u(q.prototype,"colorizeItem",function(a,c,d){a.call(this,c,d);d&&c.legendColor&&c.legendSymbol.attr({fill:c.legendColor})})})(p);(function(b){var h=b.defined,t=b.each,l=b.noop,k=b.seriesTypes;b.colorPointMixin={setVisible:function(b){var h=
|
||||
this,k=b?"show":"hide";t(["graphic","dataLabel"],function(b){if(h[b])h[b][k]()})}};b.colorSeriesMixin={pointArrayMap:["value"],axisTypes:["xAxis","yAxis","colorAxis"],optionalAxis:"colorAxis",trackerGroups:["group","markerGroup","dataLabelsGroup"],getSymbol:l,parallelArrays:["x","y","value"],colorKey:"value",pointAttribs:k.column.prototype.pointAttribs,translateColors:function(){var b=this,h=this.options.nullColor,k=this.colorAxis,l=this.colorKey;t(this.data,function(m){var f=m[l];if(f=m.options.color||
|
||||
(null===f?h:k&&void 0!==f?k.toColor(f,m):m.color||b.color))m.color=f})},colorAttribs:function(b){var k={};h(b.color)&&(k[this.colorProp||"fill"]=b.color);return k}}})(p);(function(b){var h=b.colorPointMixin,t=b.each,l=b.merge,k=b.noop,n=b.pick,p=b.Series,x=b.seriesType,q=b.seriesTypes;x("heatmap","scatter",{animation:!1,borderWidth:0,nullColor:"#f7f7f7",dataLabels:{formatter:function(){return this.point.value},inside:!0,verticalAlign:"middle",crop:!1,overflow:!1,padding:0},marker:null,pointRange:null,
|
||||
tooltip:{pointFormat:"{point.x}, {point.y}: {point.value}<br/>"},states:{normal:{animation:!0},hover:{halo:!1,brightness:.2}}},l(b.colorSeriesMixin,{pointArrayMap:["y","value"],hasPointSpecificOptions:!0,supportsDrilldown:!0,getExtremesFromAll:!0,directTouch:!0,init:function(){var b;q.scatter.prototype.init.apply(this,arguments);b=this.options;b.pointRange=n(b.pointRange,b.colsize||1);this.yAxis.axisPointRange=b.rowsize||1},translate:function(){var b=this.options,f=this.xAxis,h=this.yAxis,k=function(b,
|
||||
a,c){return Math.min(Math.max(a,b),c)};this.generatePoints();t(this.points,function(l){var a=(b.colsize||1)/2,c=(b.rowsize||1)/2,d=k(Math.round(f.len-f.translate(l.x-a,0,1,0,1)),-f.len,2*f.len),a=k(Math.round(f.len-f.translate(l.x+a,0,1,0,1)),-f.len,2*f.len),e=k(Math.round(h.translate(l.y-c,0,1,0,1)),-h.len,2*h.len),c=k(Math.round(h.translate(l.y+c,0,1,0,1)),-h.len,2*h.len);l.plotX=l.clientX=(d+a)/2;l.plotY=(e+c)/2;l.shapeType="rect";l.shapeArgs={x:Math.min(d,a),y:Math.min(e,c),width:Math.abs(a-d),
|
||||
height:Math.abs(c-e)}});this.translateColors()},drawPoints:function(){q.column.prototype.drawPoints.call(this);t(this.points,function(b){b.graphic.attr(this.colorAttribs(b,b.state))},this)},animate:k,getBox:k,drawLegendSymbol:b.LegendSymbolMixin.drawRectangle,alignDataLabel:q.column.prototype.alignDataLabel,getExtremes:function(){p.prototype.getExtremes.call(this,this.valueData);this.valueMin=this.dataMin;this.valueMax=this.dataMax;p.prototype.getExtremes.call(this)}}),h)})(p)});
|
776
high/highcharts/js/modules/heatmap.src.js
Normal file
776
high/highcharts/js/modules/heatmap.src.js
Normal file
@ -0,0 +1,776 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
*
|
||||
* (c) 2009-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* (c) 2010-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
var Axis = H.Axis,
|
||||
Chart = H.Chart,
|
||||
color = H.color,
|
||||
ColorAxis,
|
||||
each = H.each,
|
||||
extend = H.extend,
|
||||
isNumber = H.isNumber,
|
||||
Legend = H.Legend,
|
||||
LegendSymbolMixin = H.LegendSymbolMixin,
|
||||
noop = H.noop,
|
||||
merge = H.merge,
|
||||
pick = H.pick,
|
||||
wrap = H.wrap;
|
||||
|
||||
/**
|
||||
* The ColorAxis object for inclusion in gradient legends
|
||||
*/
|
||||
ColorAxis = H.ColorAxis = function() {
|
||||
this.init.apply(this, arguments);
|
||||
};
|
||||
extend(ColorAxis.prototype, Axis.prototype);
|
||||
extend(ColorAxis.prototype, {
|
||||
defaultColorAxisOptions: {
|
||||
lineWidth: 0,
|
||||
minPadding: 0,
|
||||
maxPadding: 0,
|
||||
gridLineWidth: 1,
|
||||
tickPixelInterval: 72,
|
||||
startOnTick: true,
|
||||
endOnTick: true,
|
||||
offset: 0,
|
||||
marker: {
|
||||
animation: {
|
||||
duration: 50
|
||||
},
|
||||
width: 0.01,
|
||||
|
||||
color: '#999999'
|
||||
|
||||
},
|
||||
labels: {
|
||||
overflow: 'justify'
|
||||
},
|
||||
minColor: '#e6ebf5',
|
||||
maxColor: '#003399',
|
||||
tickLength: 5,
|
||||
showInLegend: true
|
||||
},
|
||||
init: function(chart, userOptions) {
|
||||
var horiz = chart.options.legend.layout !== 'vertical',
|
||||
options;
|
||||
|
||||
this.coll = 'colorAxis';
|
||||
|
||||
// Build the options
|
||||
options = merge(this.defaultColorAxisOptions, {
|
||||
side: horiz ? 2 : 1,
|
||||
reversed: !horiz
|
||||
}, userOptions, {
|
||||
opposite: !horiz,
|
||||
showEmpty: false,
|
||||
title: null
|
||||
});
|
||||
|
||||
Axis.prototype.init.call(this, chart, options);
|
||||
|
||||
// Base init() pushes it to the xAxis array, now pop it again
|
||||
//chart[this.isXAxis ? 'xAxis' : 'yAxis'].pop();
|
||||
|
||||
// Prepare data classes
|
||||
if (userOptions.dataClasses) {
|
||||
this.initDataClasses(userOptions);
|
||||
}
|
||||
this.initStops(userOptions);
|
||||
|
||||
// Override original axis properties
|
||||
this.horiz = horiz;
|
||||
this.zoomEnabled = false;
|
||||
|
||||
// Add default values
|
||||
this.defaultLegendLength = 200;
|
||||
},
|
||||
|
||||
/*
|
||||
* Return an intermediate color between two colors, according to pos where 0
|
||||
* is the from color and 1 is the to color.
|
||||
* NOTE: Changes here should be copied
|
||||
* to the same function in drilldown.src.js and solid-gauge-src.js.
|
||||
*/
|
||||
tweenColors: function(from, to, pos) {
|
||||
// Check for has alpha, because rgba colors perform worse due to lack of
|
||||
// support in WebKit.
|
||||
var hasAlpha,
|
||||
ret;
|
||||
|
||||
// Unsupported color, return to-color (#3920)
|
||||
if (!to.rgba.length || !from.rgba.length) {
|
||||
ret = to.input || 'none';
|
||||
|
||||
// Interpolate
|
||||
} else {
|
||||
from = from.rgba;
|
||||
to = to.rgba;
|
||||
hasAlpha = (to[3] !== 1 || from[3] !== 1);
|
||||
ret = (hasAlpha ? 'rgba(' : 'rgb(') +
|
||||
Math.round(to[0] + (from[0] - to[0]) * (1 - pos)) + ',' +
|
||||
Math.round(to[1] + (from[1] - to[1]) * (1 - pos)) + ',' +
|
||||
Math.round(to[2] + (from[2] - to[2]) * (1 - pos)) +
|
||||
(hasAlpha ? (',' + (to[3] + (from[3] - to[3]) * (1 - pos))) : '') + ')';
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
initDataClasses: function(userOptions) {
|
||||
var axis = this,
|
||||
chart = this.chart,
|
||||
dataClasses,
|
||||
colorCounter = 0,
|
||||
colorCount = chart.options.chart.colorCount,
|
||||
options = this.options,
|
||||
len = userOptions.dataClasses.length;
|
||||
this.dataClasses = dataClasses = [];
|
||||
this.legendItems = [];
|
||||
|
||||
each(userOptions.dataClasses, function(dataClass, i) {
|
||||
var colors;
|
||||
|
||||
dataClass = merge(dataClass);
|
||||
dataClasses.push(dataClass);
|
||||
if (!dataClass.color) {
|
||||
if (options.dataClassColor === 'category') {
|
||||
|
||||
colors = chart.options.colors;
|
||||
colorCount = colors.length;
|
||||
dataClass.color = colors[colorCounter];
|
||||
|
||||
dataClass.colorIndex = colorCounter;
|
||||
|
||||
// increase and loop back to zero
|
||||
colorCounter++;
|
||||
if (colorCounter === colorCount) {
|
||||
colorCounter = 0;
|
||||
}
|
||||
} else {
|
||||
dataClass.color = axis.tweenColors(
|
||||
color(options.minColor),
|
||||
color(options.maxColor),
|
||||
len < 2 ? 0.5 : i / (len - 1) // #3219
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
initStops: function(userOptions) {
|
||||
this.stops = userOptions.stops || [
|
||||
[0, this.options.minColor],
|
||||
[1, this.options.maxColor]
|
||||
];
|
||||
each(this.stops, function(stop) {
|
||||
stop.color = color(stop[1]);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Extend the setOptions method to process extreme colors and color
|
||||
* stops.
|
||||
*/
|
||||
setOptions: function(userOptions) {
|
||||
Axis.prototype.setOptions.call(this, userOptions);
|
||||
|
||||
this.options.crosshair = this.options.marker;
|
||||
},
|
||||
|
||||
setAxisSize: function() {
|
||||
var symbol = this.legendSymbol,
|
||||
chart = this.chart,
|
||||
legendOptions = chart.options.legend || {},
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height;
|
||||
|
||||
if (symbol) {
|
||||
this.left = x = symbol.attr('x');
|
||||
this.top = y = symbol.attr('y');
|
||||
this.width = width = symbol.attr('width');
|
||||
this.height = height = symbol.attr('height');
|
||||
this.right = chart.chartWidth - x - width;
|
||||
this.bottom = chart.chartHeight - y - height;
|
||||
|
||||
this.len = this.horiz ? width : height;
|
||||
this.pos = this.horiz ? x : y;
|
||||
} else {
|
||||
// Fake length for disabled legend to avoid tick issues and such (#5205)
|
||||
this.len = (this.horiz ? legendOptions.symbolWidth : legendOptions.symbolHeight) || this.defaultLegendLength;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Translate from a value to a color
|
||||
*/
|
||||
toColor: function(value, point) {
|
||||
var pos,
|
||||
stops = this.stops,
|
||||
from,
|
||||
to,
|
||||
color,
|
||||
dataClasses = this.dataClasses,
|
||||
dataClass,
|
||||
i;
|
||||
|
||||
if (dataClasses) {
|
||||
i = dataClasses.length;
|
||||
while (i--) {
|
||||
dataClass = dataClasses[i];
|
||||
from = dataClass.from;
|
||||
to = dataClass.to;
|
||||
if ((from === undefined || value >= from) && (to === undefined || value <= to)) {
|
||||
color = dataClass.color;
|
||||
if (point) {
|
||||
point.dataClass = i;
|
||||
point.colorIndex = dataClass.colorIndex;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (this.isLog) {
|
||||
value = this.val2lin(value);
|
||||
}
|
||||
pos = 1 - ((this.max - value) / ((this.max - this.min) || 1));
|
||||
i = stops.length;
|
||||
while (i--) {
|
||||
if (pos > stops[i][0]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
from = stops[i] || stops[i + 1];
|
||||
to = stops[i + 1] || from;
|
||||
|
||||
// The position within the gradient
|
||||
pos = 1 - (to[0] - pos) / ((to[0] - from[0]) || 1);
|
||||
|
||||
color = this.tweenColors(
|
||||
from.color,
|
||||
to.color,
|
||||
pos
|
||||
);
|
||||
}
|
||||
return color;
|
||||
},
|
||||
|
||||
/**
|
||||
* Override the getOffset method to add the whole axis groups inside the legend.
|
||||
*/
|
||||
getOffset: function() {
|
||||
var group = this.legendGroup,
|
||||
sideOffset = this.chart.axisOffset[this.side];
|
||||
|
||||
if (group) {
|
||||
|
||||
// Hook for the getOffset method to add groups to this parent group
|
||||
this.axisParent = group;
|
||||
|
||||
// Call the base
|
||||
Axis.prototype.getOffset.call(this);
|
||||
|
||||
// First time only
|
||||
if (!this.added) {
|
||||
|
||||
this.added = true;
|
||||
|
||||
this.labelLeft = 0;
|
||||
this.labelRight = this.width;
|
||||
}
|
||||
// Reset it to avoid color axis reserving space
|
||||
this.chart.axisOffset[this.side] = sideOffset;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create the color gradient
|
||||
*/
|
||||
setLegendColor: function() {
|
||||
var grad,
|
||||
horiz = this.horiz,
|
||||
options = this.options,
|
||||
reversed = this.reversed,
|
||||
one = reversed ? 1 : 0,
|
||||
zero = reversed ? 0 : 1;
|
||||
|
||||
grad = horiz ? [one, 0, zero, 0] : [0, zero, 0, one]; // #3190
|
||||
this.legendColor = {
|
||||
linearGradient: {
|
||||
x1: grad[0],
|
||||
y1: grad[1],
|
||||
x2: grad[2],
|
||||
y2: grad[3]
|
||||
},
|
||||
stops: options.stops || [
|
||||
[0, options.minColor],
|
||||
[1, options.maxColor]
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* The color axis appears inside the legend and has its own legend symbol
|
||||
*/
|
||||
drawLegendSymbol: function(legend, item) {
|
||||
var padding = legend.padding,
|
||||
legendOptions = legend.options,
|
||||
horiz = this.horiz,
|
||||
width = pick(legendOptions.symbolWidth, horiz ? this.defaultLegendLength : 12),
|
||||
height = pick(legendOptions.symbolHeight, horiz ? 12 : this.defaultLegendLength),
|
||||
labelPadding = pick(legendOptions.labelPadding, horiz ? 16 : 30),
|
||||
itemDistance = pick(legendOptions.itemDistance, 10);
|
||||
|
||||
this.setLegendColor();
|
||||
|
||||
// Create the gradient
|
||||
item.legendSymbol = this.chart.renderer.rect(
|
||||
0,
|
||||
legend.baseline - 11,
|
||||
width,
|
||||
height
|
||||
).attr({
|
||||
zIndex: 1
|
||||
}).add(item.legendGroup);
|
||||
|
||||
// Set how much space this legend item takes up
|
||||
this.legendItemWidth = width + padding + (horiz ? itemDistance : labelPadding);
|
||||
this.legendItemHeight = height + padding + (horiz ? labelPadding : 0);
|
||||
},
|
||||
/**
|
||||
* Fool the legend
|
||||
*/
|
||||
setState: noop,
|
||||
visible: true,
|
||||
setVisible: noop,
|
||||
getSeriesExtremes: function() {
|
||||
var series;
|
||||
if (this.series.length) {
|
||||
series = this.series[0];
|
||||
this.dataMin = series.valueMin;
|
||||
this.dataMax = series.valueMax;
|
||||
}
|
||||
},
|
||||
drawCrosshair: function(e, point) {
|
||||
var plotX = point && point.plotX,
|
||||
plotY = point && point.plotY,
|
||||
crossPos,
|
||||
axisPos = this.pos,
|
||||
axisLen = this.len;
|
||||
|
||||
if (point) {
|
||||
crossPos = this.toPixels(point[point.series.colorKey]);
|
||||
if (crossPos < axisPos) {
|
||||
crossPos = axisPos - 2;
|
||||
} else if (crossPos > axisPos + axisLen) {
|
||||
crossPos = axisPos + axisLen + 2;
|
||||
}
|
||||
|
||||
point.plotX = crossPos;
|
||||
point.plotY = this.len - crossPos;
|
||||
Axis.prototype.drawCrosshair.call(this, e, point);
|
||||
point.plotX = plotX;
|
||||
point.plotY = plotY;
|
||||
|
||||
if (this.cross) {
|
||||
this.cross
|
||||
.addClass('highcharts-coloraxis-marker')
|
||||
.add(this.legendGroup);
|
||||
|
||||
|
||||
this.cross.attr({
|
||||
fill: this.crosshair.color
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
getPlotLinePath: function(a, b, c, d, pos) {
|
||||
return isNumber(pos) ? // crosshairs only // #3969 pos can be 0 !!
|
||||
(this.horiz ? ['M', pos - 4, this.top - 6, 'L', pos + 4, this.top - 6, pos, this.top, 'Z'] : ['M', this.left, pos, 'L', this.left - 6, pos + 6, this.left - 6, pos - 6, 'Z']) :
|
||||
Axis.prototype.getPlotLinePath.call(this, a, b, c, d);
|
||||
},
|
||||
|
||||
update: function(newOptions, redraw) {
|
||||
var chart = this.chart,
|
||||
legend = chart.legend;
|
||||
|
||||
each(this.series, function(series) {
|
||||
series.isDirtyData = true; // Needed for Axis.update when choropleth colors change
|
||||
});
|
||||
|
||||
// When updating data classes, destroy old items and make sure new ones are created (#3207)
|
||||
if (newOptions.dataClasses && legend.allItems) {
|
||||
each(legend.allItems, function(item) {
|
||||
if (item.isDataClass) {
|
||||
item.legendGroup.destroy();
|
||||
}
|
||||
});
|
||||
chart.isDirtyLegend = true;
|
||||
}
|
||||
|
||||
// Keep the options structure updated for export. Unlike xAxis and yAxis, the colorAxis is
|
||||
// not an array. (#3207)
|
||||
chart.options[this.coll] = merge(this.userOptions, newOptions);
|
||||
|
||||
Axis.prototype.update.call(this, newOptions, redraw);
|
||||
if (this.legendItem) {
|
||||
this.setLegendColor();
|
||||
legend.colorizeItem(this, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the legend item symbols for data classes
|
||||
*/
|
||||
getDataClassLegendSymbols: function() {
|
||||
var axis = this,
|
||||
chart = this.chart,
|
||||
legendItems = this.legendItems,
|
||||
legendOptions = chart.options.legend,
|
||||
valueDecimals = legendOptions.valueDecimals,
|
||||
valueSuffix = legendOptions.valueSuffix || '',
|
||||
name;
|
||||
|
||||
if (!legendItems.length) {
|
||||
each(this.dataClasses, function(dataClass, i) {
|
||||
var vis = true,
|
||||
from = dataClass.from,
|
||||
to = dataClass.to;
|
||||
|
||||
// Assemble the default name. This can be overridden by legend.options.labelFormatter
|
||||
name = '';
|
||||
if (from === undefined) {
|
||||
name = '< ';
|
||||
} else if (to === undefined) {
|
||||
name = '> ';
|
||||
}
|
||||
if (from !== undefined) {
|
||||
name += H.numberFormat(from, valueDecimals) + valueSuffix;
|
||||
}
|
||||
if (from !== undefined && to !== undefined) {
|
||||
name += ' - ';
|
||||
}
|
||||
if (to !== undefined) {
|
||||
name += H.numberFormat(to, valueDecimals) + valueSuffix;
|
||||
}
|
||||
// Add a mock object to the legend items
|
||||
legendItems.push(extend({
|
||||
chart: chart,
|
||||
name: name,
|
||||
options: {},
|
||||
drawLegendSymbol: LegendSymbolMixin.drawRectangle,
|
||||
visible: true,
|
||||
setState: noop,
|
||||
isDataClass: true,
|
||||
setVisible: function() {
|
||||
vis = this.visible = !vis;
|
||||
each(axis.series, function(series) {
|
||||
each(series.points, function(point) {
|
||||
if (point.dataClass === i) {
|
||||
point.setVisible(vis);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
chart.legend.colorizeItem(this, vis);
|
||||
}
|
||||
}, dataClass));
|
||||
});
|
||||
}
|
||||
return legendItems;
|
||||
},
|
||||
name: '' // Prevents 'undefined' in legend in IE8
|
||||
});
|
||||
|
||||
/**
|
||||
* Handle animation of the color attributes directly
|
||||
*/
|
||||
each(['fill', 'stroke'], function(prop) {
|
||||
H.Fx.prototype[prop + 'Setter'] = function() {
|
||||
this.elem.attr(prop, ColorAxis.prototype.tweenColors(color(this.start), color(this.end), this.pos));
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Extend the chart getAxes method to also get the color axis
|
||||
*/
|
||||
wrap(Chart.prototype, 'getAxes', function(proceed) {
|
||||
|
||||
var options = this.options,
|
||||
colorAxisOptions = options.colorAxis;
|
||||
|
||||
proceed.call(this);
|
||||
|
||||
this.colorAxis = [];
|
||||
if (colorAxisOptions) {
|
||||
new ColorAxis(this, colorAxisOptions); // eslint-disable-line no-new
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Wrap the legend getAllItems method to add the color axis. This also removes the
|
||||
* axis' own series to prevent them from showing up individually.
|
||||
*/
|
||||
wrap(Legend.prototype, 'getAllItems', function(proceed) {
|
||||
var allItems = [],
|
||||
colorAxis = this.chart.colorAxis[0];
|
||||
|
||||
if (colorAxis && colorAxis.options) {
|
||||
if (colorAxis.options.showInLegend) {
|
||||
// Data classes
|
||||
if (colorAxis.options.dataClasses) {
|
||||
allItems = allItems.concat(colorAxis.getDataClassLegendSymbols());
|
||||
// Gradient legend
|
||||
} else {
|
||||
// Add this axis on top
|
||||
allItems.push(colorAxis);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't add the color axis' series
|
||||
each(colorAxis.series, function(series) {
|
||||
series.options.showInLegend = false;
|
||||
});
|
||||
}
|
||||
|
||||
return allItems.concat(proceed.call(this));
|
||||
});
|
||||
|
||||
wrap(Legend.prototype, 'colorizeItem', function(proceed, item, visible) {
|
||||
proceed.call(this, item, visible);
|
||||
if (visible && item.legendColor) {
|
||||
item.legendSymbol.attr({
|
||||
fill: item.legendColor
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
(function(H) {
|
||||
/**
|
||||
* (c) 2010-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
var defined = H.defined,
|
||||
each = H.each,
|
||||
noop = H.noop,
|
||||
seriesTypes = H.seriesTypes;
|
||||
|
||||
/**
|
||||
* Mixin for maps and heatmaps
|
||||
*/
|
||||
H.colorPointMixin = {
|
||||
/**
|
||||
* Set the visibility of a single point
|
||||
*/
|
||||
setVisible: function(vis) {
|
||||
var point = this,
|
||||
method = vis ? 'show' : 'hide';
|
||||
|
||||
// Show and hide associated elements
|
||||
each(['graphic', 'dataLabel'], function(key) {
|
||||
if (point[key]) {
|
||||
point[key][method]();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
H.colorSeriesMixin = {
|
||||
pointArrayMap: ['value'],
|
||||
axisTypes: ['xAxis', 'yAxis', 'colorAxis'],
|
||||
optionalAxis: 'colorAxis',
|
||||
trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
|
||||
getSymbol: noop,
|
||||
parallelArrays: ['x', 'y', 'value'],
|
||||
colorKey: 'value',
|
||||
|
||||
|
||||
pointAttribs: seriesTypes.column.prototype.pointAttribs,
|
||||
|
||||
|
||||
/**
|
||||
* In choropleth maps, the color is a result of the value, so this needs translation too
|
||||
*/
|
||||
translateColors: function() {
|
||||
var series = this,
|
||||
nullColor = this.options.nullColor,
|
||||
colorAxis = this.colorAxis,
|
||||
colorKey = this.colorKey;
|
||||
|
||||
each(this.data, function(point) {
|
||||
var value = point[colorKey],
|
||||
color;
|
||||
|
||||
color = point.options.color ||
|
||||
(value === null ? nullColor : (colorAxis && value !== undefined) ? colorAxis.toColor(value, point) : point.color || series.color);
|
||||
|
||||
if (color) {
|
||||
point.color = color;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the color attibutes to apply on the graphic
|
||||
*/
|
||||
colorAttribs: function(point) {
|
||||
var ret = {};
|
||||
if (defined(point.color)) {
|
||||
ret[this.colorProp || 'fill'] = point.color;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
}(Highcharts));
|
||||
(function(H) {
|
||||
/**
|
||||
* (c) 2010-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
var colorPointMixin = H.colorPointMixin,
|
||||
colorSeriesMixin = H.colorSeriesMixin,
|
||||
each = H.each,
|
||||
LegendSymbolMixin = H.LegendSymbolMixin,
|
||||
merge = H.merge,
|
||||
noop = H.noop,
|
||||
pick = H.pick,
|
||||
Series = H.Series,
|
||||
seriesType = H.seriesType,
|
||||
seriesTypes = H.seriesTypes;
|
||||
|
||||
// The Heatmap series type
|
||||
seriesType('heatmap', 'scatter', {
|
||||
animation: false,
|
||||
borderWidth: 0,
|
||||
|
||||
nullColor: '#f7f7f7',
|
||||
|
||||
dataLabels: {
|
||||
formatter: function() { // #2945
|
||||
return this.point.value;
|
||||
},
|
||||
inside: true,
|
||||
verticalAlign: 'middle',
|
||||
crop: false,
|
||||
overflow: false,
|
||||
padding: 0 // #3837
|
||||
},
|
||||
marker: null,
|
||||
pointRange: null, // dynamically set to colsize by default
|
||||
tooltip: {
|
||||
pointFormat: '{point.x}, {point.y}: {point.value}<br/>'
|
||||
},
|
||||
states: {
|
||||
normal: {
|
||||
animation: true
|
||||
},
|
||||
hover: {
|
||||
halo: false, // #3406, halo is not required on heatmaps
|
||||
brightness: 0.2
|
||||
}
|
||||
}
|
||||
}, merge(colorSeriesMixin, {
|
||||
pointArrayMap: ['y', 'value'],
|
||||
hasPointSpecificOptions: true,
|
||||
supportsDrilldown: true,
|
||||
getExtremesFromAll: true,
|
||||
directTouch: true,
|
||||
|
||||
/**
|
||||
* Override the init method to add point ranges on both axes.
|
||||
*/
|
||||
init: function() {
|
||||
var options;
|
||||
seriesTypes.scatter.prototype.init.apply(this, arguments);
|
||||
|
||||
options = this.options;
|
||||
options.pointRange = pick(options.pointRange, options.colsize || 1); // #3758, prevent resetting in setData
|
||||
this.yAxis.axisPointRange = options.rowsize || 1; // general point range
|
||||
},
|
||||
translate: function() {
|
||||
var series = this,
|
||||
options = series.options,
|
||||
xAxis = series.xAxis,
|
||||
yAxis = series.yAxis,
|
||||
between = function(x, a, b) {
|
||||
return Math.min(Math.max(a, x), b);
|
||||
};
|
||||
|
||||
series.generatePoints();
|
||||
|
||||
each(series.points, function(point) {
|
||||
var xPad = (options.colsize || 1) / 2,
|
||||
yPad = (options.rowsize || 1) / 2,
|
||||
x1 = between(Math.round(xAxis.len - xAxis.translate(point.x - xPad, 0, 1, 0, 1)), -xAxis.len, 2 * xAxis.len),
|
||||
x2 = between(Math.round(xAxis.len - xAxis.translate(point.x + xPad, 0, 1, 0, 1)), -xAxis.len, 2 * xAxis.len),
|
||||
y1 = between(Math.round(yAxis.translate(point.y - yPad, 0, 1, 0, 1)), -yAxis.len, 2 * yAxis.len),
|
||||
y2 = between(Math.round(yAxis.translate(point.y + yPad, 0, 1, 0, 1)), -yAxis.len, 2 * yAxis.len);
|
||||
|
||||
// Set plotX and plotY for use in K-D-Tree and more
|
||||
point.plotX = point.clientX = (x1 + x2) / 2;
|
||||
point.plotY = (y1 + y2) / 2;
|
||||
|
||||
point.shapeType = 'rect';
|
||||
point.shapeArgs = {
|
||||
x: Math.min(x1, x2),
|
||||
y: Math.min(y1, y2),
|
||||
width: Math.abs(x2 - x1),
|
||||
height: Math.abs(y2 - y1)
|
||||
};
|
||||
});
|
||||
|
||||
series.translateColors();
|
||||
},
|
||||
drawPoints: function() {
|
||||
seriesTypes.column.prototype.drawPoints.call(this);
|
||||
|
||||
each(this.points, function(point) {
|
||||
point.graphic.attr(this.colorAttribs(point, point.state));
|
||||
}, this);
|
||||
},
|
||||
animate: noop,
|
||||
getBox: noop,
|
||||
drawLegendSymbol: LegendSymbolMixin.drawRectangle,
|
||||
alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
|
||||
getExtremes: function() {
|
||||
// Get the extremes from the value data
|
||||
Series.prototype.getExtremes.call(this, this.valueData);
|
||||
this.valueMin = this.dataMin;
|
||||
this.valueMax = this.dataMax;
|
||||
|
||||
// Get the extremes from the y data
|
||||
Series.prototype.getExtremes.call(this);
|
||||
}
|
||||
|
||||
}), colorPointMixin);
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
12
high/highcharts/js/modules/no-data-to-display.js
Normal file
12
high/highcharts/js/modules/no-data-to-display.js
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
Plugin for displaying a message when there is no data visible in chart.
|
||||
|
||||
(c) 2010-2016 Highsoft AS
|
||||
Author: Oystein Moseng
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(d){"object"===typeof module&&module.exports?module.exports=d:d(Highcharts)})(function(d){(function(c){function d(){return!!this.points.length}function g(){this.hasData()?this.hideNoData():this.showNoData()}var h=c.seriesTypes,e=c.Chart.prototype,f=c.getOptions(),k=c.extend,l=c.each;k(f.lang,{noData:"No data to display"});f.noData={position:{x:0,y:0,align:"center",verticalAlign:"middle"}};f.noData.style={fontWeight:"bold",fontSize:"12px",color:"#666666"};l(["pie","gauge","waterfall","bubble",
|
||||
"treemap"],function(a){h[a]&&(h[a].prototype.hasData=d)});c.Series.prototype.hasData=function(){return this.visible&&void 0!==this.dataMax&&void 0!==this.dataMin};e.showNoData=function(a){var b=this.options;a=a||b.lang.noData;b=b.noData;this.noDataLabel||(this.noDataLabel=this.renderer.label(a,0,0,null,null,null,b.useHTML,null,"no-data"),this.noDataLabel.attr(b.attr).css(b.style),this.noDataLabel.add(),this.noDataLabel.align(k(this.noDataLabel.getBBox(),b.position),!1,"plotBox"))};e.hideNoData=function(){this.noDataLabel&&
|
||||
(this.noDataLabel=this.noDataLabel.destroy())};e.hasData=function(){for(var a=this.series,b=a.length;b--;)if(a[b].hasData()&&!a[b].options.isInternal)return!0;return!1};e.callbacks.push(function(a){c.addEvent(a,"load",g);c.addEvent(a,"redraw",g)})})(d)});
|
161
high/highcharts/js/modules/no-data-to-display.src.js
Normal file
161
high/highcharts/js/modules/no-data-to-display.src.js
Normal file
@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
* Plugin for displaying a message when there is no data visible in chart.
|
||||
*
|
||||
* (c) 2010-2016 Highsoft AS
|
||||
* Author: Oystein Moseng
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* Plugin for displaying a message when there is no data visible in chart.
|
||||
*
|
||||
* (c) 2010-2016 Highsoft AS
|
||||
* Author: Oystein Moseng
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var seriesTypes = H.seriesTypes,
|
||||
chartPrototype = H.Chart.prototype,
|
||||
defaultOptions = H.getOptions(),
|
||||
extend = H.extend,
|
||||
each = H.each;
|
||||
|
||||
// Add language option
|
||||
extend(defaultOptions.lang, {
|
||||
noData: 'No data to display'
|
||||
});
|
||||
|
||||
// Add default display options for message
|
||||
defaultOptions.noData = {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
align: 'center',
|
||||
verticalAlign: 'middle'
|
||||
}
|
||||
// useHTML: false
|
||||
};
|
||||
|
||||
|
||||
// Presentational
|
||||
defaultOptions.noData.style = {
|
||||
fontWeight: 'bold',
|
||||
fontSize: '12px',
|
||||
color: '#666666'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Define hasData functions for series. These return true if there are data points on this series within the plot area
|
||||
*/
|
||||
function hasDataPie() {
|
||||
return !!this.points.length; /* != 0 */
|
||||
}
|
||||
|
||||
each(['pie', 'gauge', 'waterfall', 'bubble', 'treemap'], function(type) {
|
||||
if (seriesTypes[type]) {
|
||||
seriesTypes[type].prototype.hasData = hasDataPie;
|
||||
}
|
||||
});
|
||||
|
||||
H.Series.prototype.hasData = function() {
|
||||
return this.visible && this.dataMax !== undefined && this.dataMin !== undefined; // #3703
|
||||
};
|
||||
|
||||
/**
|
||||
* Display a no-data message.
|
||||
*
|
||||
* @param {String} str An optional message to show in place of the default one
|
||||
*/
|
||||
chartPrototype.showNoData = function(str) {
|
||||
var chart = this,
|
||||
options = chart.options,
|
||||
text = str || options.lang.noData,
|
||||
noDataOptions = options.noData;
|
||||
|
||||
if (!chart.noDataLabel) {
|
||||
chart.noDataLabel = chart.renderer
|
||||
.label(
|
||||
text,
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
noDataOptions.useHTML,
|
||||
null,
|
||||
'no-data'
|
||||
);
|
||||
|
||||
|
||||
chart.noDataLabel
|
||||
.attr(noDataOptions.attr)
|
||||
.css(noDataOptions.style);
|
||||
|
||||
|
||||
chart.noDataLabel.add();
|
||||
|
||||
chart.noDataLabel.align(extend(chart.noDataLabel.getBBox(), noDataOptions.position), false, 'plotBox');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide no-data message
|
||||
*/
|
||||
chartPrototype.hideNoData = function() {
|
||||
var chart = this;
|
||||
if (chart.noDataLabel) {
|
||||
chart.noDataLabel = chart.noDataLabel.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if there are data points within the plot area now
|
||||
*/
|
||||
chartPrototype.hasData = function() {
|
||||
var chart = this,
|
||||
series = chart.series,
|
||||
i = series.length;
|
||||
|
||||
while (i--) {
|
||||
if (series[i].hasData() && !series[i].options.isInternal) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Show no-data message if there is no data in sight. Otherwise, hide it.
|
||||
*/
|
||||
function handleNoData() {
|
||||
var chart = this;
|
||||
if (chart.hasData()) {
|
||||
chart.hideNoData();
|
||||
} else {
|
||||
chart.showNoData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event listener to handle automatic display of no-data message
|
||||
*/
|
||||
chartPrototype.callbacks.push(function(chart) {
|
||||
H.addEvent(chart, 'load', handleNoData);
|
||||
H.addEvent(chart, 'redraw', handleNoData);
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
16
high/highcharts/js/modules/offline-exporting.js
Normal file
16
high/highcharts/js/modules/offline-exporting.js
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
Client side exporting module
|
||||
|
||||
(c) 2015 Torstein Honsi / Oystein Moseng
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(r){"object"===typeof module&&module.exports?module.exports=r:r(Highcharts)})(function(r){(function(a){function r(e,a){var c=t.getElementsByTagName("head")[0],b=t.createElement("script");b.type="text/javascript";b.src=e;b.onload=a;c.appendChild(b)}var A=a.merge,m=a.win,q=m.navigator,t=m.document,v=m.URL||m.webkitURL||m,w=/Edge\/|Trident\/|MSIE /.test(q.userAgent),B=w?150:0;a.CanVGRenderer={};a.downloadURL=function(e,a){var c=t.createElement("a"),b;if(q.msSaveOrOpenBlob)q.msSaveOrOpenBlob(e,
|
||||
a);else if(void 0!==c.download)c.href=e,c.download=a,c.target="_blank",t.body.appendChild(c),c.click(),t.body.removeChild(c);else try{if(b=m.open(e,"chart"),void 0===b||null===b)throw"Failed to open window";}catch(h){m.location.href=e}};a.svgToDataUrl=function(e){var a=-1<q.userAgent.indexOf("WebKit")&&0>q.userAgent.indexOf("Chrome");try{if(!a&&0>q.userAgent.toLowerCase().indexOf("firefox"))return v.createObjectURL(new m.Blob([e],{type:"image/svg+xml;charset-utf-16"}))}catch(c){}return"data:image/svg+xml;charset=UTF-8,"+
|
||||
encodeURIComponent(e)};a.imageToDataUrl=function(a,k,c,b,h,d,l,g,p){var f=new m.Image,n,u=function(){setTimeout(function(){var d=t.createElement("canvas"),u=d.getContext&&d.getContext("2d"),g;try{if(u){d.height=f.height*b;d.width=f.width*b;u.drawImage(f,0,0,d.width,d.height);try{g=d.toDataURL(k),h(g,k,c,b)}catch(m){n(a,k,c,b)}}else l(a,k,c,b)}finally{p&&p(a,k,c,b)}},B)},z=function(){g(a,k,c,b);p&&p(a,k,c,b)};n=function(){f=new m.Image;n=d;f.crossOrigin="Anonymous";f.onload=u;f.onerror=z;f.src=a};
|
||||
f.onload=u;f.onerror=z;f.src=a};a.downloadSVGLocal=function(e,k,c,b,h,d){var l,g,p=!0,f,n=a.getOptions().exporting.libURL;if("image/svg+xml"===c)try{q.msSaveOrOpenBlob?(g=new MSBlobBuilder,g.append(e),l=g.getBlob("image/svg+xml")):l=a.svgToDataUrl(e),a.downloadURL(l,k),d&&d()}catch(u){h()}else l=a.svgToDataUrl(e),f=function(){try{v.revokeObjectURL(l)}catch(a){}},a.imageToDataUrl(l,c,{},b,function(b){try{a.downloadURL(b,k),d&&d()}catch(c){h()}},function(){var g=t.createElement("canvas"),l=g.getContext("2d"),
|
||||
x=e.match(/^<svg[^>]*width\s*=\s*\"?(\d+)\"?[^>]*>/)[1]*b,y=e.match(/^<svg[^>]*height\s*=\s*\"?(\d+)\"?[^>]*>/)[1]*b,v=function(){l.drawSvg(e,0,0,x,y);try{a.downloadURL(q.msSaveOrOpenBlob?g.msToBlob():g.toDataURL(c),k),d&&d()}catch(b){h()}finally{f()}};g.width=x;g.height=y;m.canvg?v():(p=!0,n="/"!==n.substr[-1]?n+"/":n,r(n+"rgbcolor.js",function(){r(n+"canvg.js",function(){v()})}))},h,h,function(){p&&f()})};a.Chart.prototype.getSVGForLocalExport=function(e,k,c,b){var h=this,d,l=0,g,p,f,n,m=function(a,
|
||||
c,e){++l;e.imageElement.setAttributeNS("http://www.w3.org/1999/xlink","href",a);l===d.length&&b(h.sanitizeSVG(g.innerHTML))};a.wrap(a.Chart.prototype,"getChartHTML",function(a){g=this.container.cloneNode(!0);return a.apply(this,Array.prototype.slice.call(arguments,1))});h.getSVGForExport(e,k);d=g.getElementsByTagName("image");try{if(d.length)for(f=0,n=d.length;f<n;++f)p=d[f],a.imageToDataUrl(p.getAttributeNS("http://www.w3.org/1999/xlink","href"),"image/png",{imageElement:p},e.scale,m,c,c,c);else b(h.sanitizeSVG(g.innerHTML))}catch(q){c()}};
|
||||
a.Chart.prototype.exportChartLocal=function(e,k){var c=this,b=a.merge(c.options.exporting,e),h=b&&b.type||"image/png",d=function(){if(!1===b.fallbackToExportServer)if(b.error)b.error();else throw"Fallback to export server disabled";else c.exportChart(b)},l=function(c){var e=(b.filename||"chart")+"."+("image/svg+xml"===h?"svg":h.split("/")[1]);a.downloadSVGLocal(c,e,h,b.scale,d)};w&&"image/svg+xml"!==h&&c.container.getElementsByTagName("image").length?d():c.getSVGForLocalExport(b,k,d,l)};A(!0,a.getOptions().exporting,
|
||||
{libURL:"http://code.highcharts.com@product.cdnpath@/5.0.0/lib/",buttons:{contextButton:{menuItems:[{textKey:"printChart",onclick:function(){this.print()}},{separator:!0},{textKey:"downloadPNG",onclick:function(){this.exportChartLocal()}},{textKey:"downloadJPEG",onclick:function(){this.exportChartLocal({type:"image/jpeg"})}},{textKey:"downloadSVG",onclick:function(){this.exportChartLocal({type:"image/svg+xml"})}}]}}})})(r)});
|
424
high/highcharts/js/modules/offline-exporting.src.js
Normal file
424
high/highcharts/js/modules/offline-exporting.src.js
Normal file
@ -0,0 +1,424 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
* Client side exporting module
|
||||
*
|
||||
* (c) 2015 Torstein Honsi / Oystein Moseng
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(Highcharts) {
|
||||
/**
|
||||
* Client side exporting module
|
||||
*
|
||||
* (c) 2015 Torstein Honsi / Oystein Moseng
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
/*global MSBlobBuilder */
|
||||
|
||||
var merge = Highcharts.merge,
|
||||
win = Highcharts.win,
|
||||
nav = win.navigator,
|
||||
doc = win.document,
|
||||
domurl = win.URL || win.webkitURL || win,
|
||||
isMSBrowser = /Edge\/|Trident\/|MSIE /.test(nav.userAgent),
|
||||
loadEventDeferDelay = isMSBrowser ? 150 : 0; // Milliseconds to defer image load event handlers to offset IE bug
|
||||
|
||||
// Dummy object so we can reuse our canvas-tools.js without errors
|
||||
Highcharts.CanVGRenderer = {};
|
||||
|
||||
|
||||
/**
|
||||
* Downloads a script and executes a callback when done.
|
||||
* @param {String} scriptLocation
|
||||
* @param {Function} callback
|
||||
*/
|
||||
function getScript(scriptLocation, callback) {
|
||||
var head = doc.getElementsByTagName('head')[0],
|
||||
script = doc.createElement('script');
|
||||
|
||||
script.type = 'text/javascript';
|
||||
script.src = scriptLocation;
|
||||
script.onload = callback;
|
||||
|
||||
head.appendChild(script);
|
||||
}
|
||||
|
||||
// Download contents by dataURL/blob
|
||||
Highcharts.downloadURL = function(dataURL, filename) {
|
||||
var a = doc.createElement('a'),
|
||||
windowRef;
|
||||
|
||||
// IE specific blob implementation
|
||||
if (nav.msSaveOrOpenBlob) {
|
||||
nav.msSaveOrOpenBlob(dataURL, filename);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try HTML5 download attr if supported
|
||||
if (a.download !== undefined) {
|
||||
a.href = dataURL;
|
||||
a.download = filename; // HTML5 download attribute
|
||||
a.target = '_blank';
|
||||
doc.body.appendChild(a);
|
||||
a.click();
|
||||
doc.body.removeChild(a);
|
||||
} else {
|
||||
// No download attr, just opening data URI
|
||||
try {
|
||||
windowRef = win.open(dataURL, 'chart');
|
||||
if (windowRef === undefined || windowRef === null) {
|
||||
throw 'Failed to open window';
|
||||
}
|
||||
} catch (e) {
|
||||
// window.open failed, trying location.href
|
||||
win.location.href = dataURL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Get blob URL from SVG code. Falls back to normal data URI.
|
||||
Highcharts.svgToDataUrl = function(svg) {
|
||||
var webKit = nav.userAgent.indexOf('WebKit') > -1 && nav.userAgent.indexOf('Chrome') < 0; // Webkit and not chrome
|
||||
try {
|
||||
// Safari requires data URI since it doesn't allow navigation to blob URLs
|
||||
// Firefox has an issue with Blobs and internal references, leading to gradients not working using Blobs (#4550)
|
||||
if (!webKit && nav.userAgent.toLowerCase().indexOf('firefox') < 0) {
|
||||
return domurl.createObjectURL(new win.Blob([svg], {
|
||||
type: 'image/svg+xml;charset-utf-16'
|
||||
}));
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
|
||||
};
|
||||
|
||||
// Get data:URL from image URL
|
||||
// Pass in callbacks to handle results. finallyCallback is always called at the end of the process. Supplying this callback is optional.
|
||||
// All callbacks receive four arguments: imageURL, imageType, callbackArgs and scale. callbackArgs is used only by callbacks and can contain whatever.
|
||||
Highcharts.imageToDataUrl = function(imageURL, imageType, callbackArgs, scale, successCallback, taintedCallback, noCanvasSupportCallback, failedLoadCallback, finallyCallback) {
|
||||
var img = new win.Image(),
|
||||
taintedHandler,
|
||||
loadHandler = function() {
|
||||
setTimeout(function() {
|
||||
var canvas = doc.createElement('canvas'),
|
||||
ctx = canvas.getContext && canvas.getContext('2d'),
|
||||
dataURL;
|
||||
try {
|
||||
if (!ctx) {
|
||||
noCanvasSupportCallback(imageURL, imageType, callbackArgs, scale);
|
||||
} else {
|
||||
canvas.height = img.height * scale;
|
||||
canvas.width = img.width * scale;
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Now we try to get the contents of the canvas.
|
||||
try {
|
||||
dataURL = canvas.toDataURL(imageType);
|
||||
successCallback(dataURL, imageType, callbackArgs, scale);
|
||||
} catch (e) {
|
||||
taintedHandler(imageURL, imageType, callbackArgs, scale);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (finallyCallback) {
|
||||
finallyCallback(imageURL, imageType, callbackArgs, scale);
|
||||
}
|
||||
}
|
||||
}, loadEventDeferDelay); // IE bug where image is not always ready despite calling load event.
|
||||
},
|
||||
// Image load failed (e.g. invalid URL)
|
||||
errorHandler = function() {
|
||||
failedLoadCallback(imageURL, imageType, callbackArgs, scale);
|
||||
if (finallyCallback) {
|
||||
finallyCallback(imageURL, imageType, callbackArgs, scale);
|
||||
}
|
||||
};
|
||||
|
||||
// This is called on load if the image drawing to canvas failed with a security error.
|
||||
// We retry the drawing with crossOrigin set to Anonymous.
|
||||
taintedHandler = function() {
|
||||
img = new win.Image();
|
||||
taintedHandler = taintedCallback;
|
||||
img.crossOrigin = 'Anonymous'; // Must be set prior to loading image source
|
||||
img.onload = loadHandler;
|
||||
img.onerror = errorHandler;
|
||||
img.src = imageURL;
|
||||
};
|
||||
|
||||
img.onload = loadHandler;
|
||||
img.onerror = errorHandler;
|
||||
img.src = imageURL;
|
||||
};
|
||||
|
||||
// Get data URL to an image of an SVG and call download on it
|
||||
Highcharts.downloadSVGLocal = function(svg, filename, imageType, scale, failCallback, successCallback) {
|
||||
var svgurl,
|
||||
blob,
|
||||
objectURLRevoke = true,
|
||||
finallyHandler,
|
||||
libURL = Highcharts.getOptions().exporting.libURL;
|
||||
|
||||
/*
|
||||
function svgToPdf(svgElement, margin) {
|
||||
var width = svgElement.width.baseVal.value + 2 * margin;
|
||||
var height = svgElement.height.baseVal.value + 2 * margin;
|
||||
var pdf = new win.jsPDF('l', 'pt', [width, height]); // eslint-disable-line new-cap
|
||||
win.svgElementToPdf(svgElement, pdf, { removeInvalid: true });
|
||||
return pdf.output('datauristring');
|
||||
}
|
||||
*/
|
||||
|
||||
// Initiate download depending on file type
|
||||
if (imageType === 'image/svg+xml') {
|
||||
// SVG download. In this case, we want to use Microsoft specific Blob if available
|
||||
try {
|
||||
if (nav.msSaveOrOpenBlob) {
|
||||
blob = new MSBlobBuilder();
|
||||
blob.append(svg);
|
||||
svgurl = blob.getBlob('image/svg+xml');
|
||||
} else {
|
||||
svgurl = Highcharts.svgToDataUrl(svg);
|
||||
}
|
||||
Highcharts.downloadURL(svgurl, filename);
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
} catch (e) {
|
||||
failCallback();
|
||||
}
|
||||
/*} else if (imageType === 'application/pdf') {
|
||||
doc.getElementsByTagName('svg')[0].id = 'svgElement';
|
||||
// you should set the format dynamically, write [width, height] instead of 'a4'
|
||||
if (win.jsPDF && win.svgElementToPdf) {
|
||||
var dummyContainer = doc.createElement('div');
|
||||
dummyContainer.innerHTML = svg;
|
||||
setTimeout(function () {
|
||||
var data = svgToPdf(dummyContainer.firstChild, 0);
|
||||
Highcharts.downloadURL(data, filename);
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
}, 100);
|
||||
}*/
|
||||
} else {
|
||||
// PNG/JPEG download - create bitmap from SVG
|
||||
|
||||
svgurl = Highcharts.svgToDataUrl(svg);
|
||||
finallyHandler = function() {
|
||||
try {
|
||||
domurl.revokeObjectURL(svgurl);
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
};
|
||||
// First, try to get PNG by rendering on canvas
|
||||
Highcharts.imageToDataUrl(svgurl, imageType, { /* args */ }, scale, function(imageURL) {
|
||||
// Success
|
||||
try {
|
||||
Highcharts.downloadURL(imageURL, filename);
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
} catch (e) {
|
||||
failCallback();
|
||||
}
|
||||
}, function() {
|
||||
// Failed due to tainted canvas
|
||||
// Create new and untainted canvas
|
||||
var canvas = doc.createElement('canvas'),
|
||||
ctx = canvas.getContext('2d'),
|
||||
imageWidth = svg.match(/^<svg[^>]*width\s*=\s*\"?(\d+)\"?[^>]*>/)[1] * scale,
|
||||
imageHeight = svg.match(/^<svg[^>]*height\s*=\s*\"?(\d+)\"?[^>]*>/)[1] * scale,
|
||||
downloadWithCanVG = function() {
|
||||
ctx.drawSvg(svg, 0, 0, imageWidth, imageHeight);
|
||||
try {
|
||||
Highcharts.downloadURL(nav.msSaveOrOpenBlob ? canvas.msToBlob() : canvas.toDataURL(imageType), filename);
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
} catch (e) {
|
||||
failCallback();
|
||||
} finally {
|
||||
finallyHandler();
|
||||
}
|
||||
};
|
||||
|
||||
canvas.width = imageWidth;
|
||||
canvas.height = imageHeight;
|
||||
if (win.canvg) {
|
||||
// Use preloaded canvg
|
||||
downloadWithCanVG();
|
||||
} else {
|
||||
// Must load canVG first
|
||||
objectURLRevoke = true; // Don't destroy the object URL yet since we are doing things asynchronously. A cleaner solution would be nice, but this will do for now.
|
||||
libURL = libURL.substr[-1] !== '/' ? libURL + '/' : libURL; // Allow libURL to end with or without fordward slash
|
||||
getScript(libURL + 'rgbcolor.js', function() { // Get RGBColor.js first
|
||||
getScript(libURL + 'canvg.js', function() {
|
||||
downloadWithCanVG();
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
// No canvas support
|
||||
failCallback,
|
||||
// Failed to load image
|
||||
failCallback,
|
||||
// Finally
|
||||
function() {
|
||||
if (objectURLRevoke) {
|
||||
finallyHandler();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get SVG of chart prepared for client side export. This converts embedded images in the SVG to data URIs.
|
||||
// The options and chartOptions arguments are passed to the getSVGForExport function.
|
||||
Highcharts.Chart.prototype.getSVGForLocalExport = function(options, chartOptions, failCallback, successCallback) {
|
||||
var chart = this,
|
||||
images,
|
||||
imagesEmbedded = 0,
|
||||
chartCopyContainer,
|
||||
el,
|
||||
i,
|
||||
l,
|
||||
// Success handler, we converted image to base64!
|
||||
embeddedSuccess = function(imageURL, imageType, callbackArgs) {
|
||||
++imagesEmbedded;
|
||||
|
||||
// Change image href in chart copy
|
||||
callbackArgs.imageElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', imageURL);
|
||||
|
||||
// When done with last image we have our SVG
|
||||
if (imagesEmbedded === images.length) {
|
||||
successCallback(chart.sanitizeSVG(chartCopyContainer.innerHTML));
|
||||
}
|
||||
};
|
||||
|
||||
// Hook into getSVG to get a copy of the chart copy's container
|
||||
Highcharts.wrap(Highcharts.Chart.prototype, 'getChartHTML', function(proceed) {
|
||||
chartCopyContainer = this.container.cloneNode(true);
|
||||
return proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
});
|
||||
|
||||
// Trigger hook to get chart copy
|
||||
chart.getSVGForExport(options, chartOptions);
|
||||
images = chartCopyContainer.getElementsByTagName('image');
|
||||
|
||||
try {
|
||||
// If there are no images to embed, the SVG is okay now.
|
||||
if (!images.length) {
|
||||
successCallback(chart.sanitizeSVG(chartCopyContainer.innerHTML)); // Use SVG of chart copy
|
||||
return;
|
||||
}
|
||||
|
||||
// Go through the images we want to embed
|
||||
for (i = 0, l = images.length; i < l; ++i) {
|
||||
el = images[i];
|
||||
Highcharts.imageToDataUrl(el.getAttributeNS('http://www.w3.org/1999/xlink', 'href'), 'image/png', {
|
||||
imageElement: el
|
||||
}, options.scale,
|
||||
embeddedSuccess,
|
||||
// Tainted canvas
|
||||
failCallback,
|
||||
// No canvas support
|
||||
failCallback,
|
||||
// Failed to load source
|
||||
failCallback
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
failCallback();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a new method to the Chart object to perform a local download
|
||||
*/
|
||||
Highcharts.Chart.prototype.exportChartLocal = function(exportingOptions, chartOptions) {
|
||||
var chart = this,
|
||||
options = Highcharts.merge(chart.options.exporting, exportingOptions),
|
||||
imageType = options && options.type || 'image/png',
|
||||
fallbackToExportServer = function() {
|
||||
if (options.fallbackToExportServer === false) {
|
||||
if (options.error) {
|
||||
options.error();
|
||||
} else {
|
||||
throw 'Fallback to export server disabled';
|
||||
}
|
||||
} else {
|
||||
chart.exportChart(options);
|
||||
}
|
||||
},
|
||||
svgSuccess = function(svg) {
|
||||
var filename = (options.filename || 'chart') + '.' + (imageType === 'image/svg+xml' ? 'svg' : imageType.split('/')[1]);
|
||||
Highcharts.downloadSVGLocal(svg, filename, imageType, options.scale, fallbackToExportServer);
|
||||
};
|
||||
|
||||
// If we have embedded images and are exporting to JPEG/PNG, Microsoft browsers won't handle it, so fall back
|
||||
if (isMSBrowser && imageType !== 'image/svg+xml' && chart.container.getElementsByTagName('image').length) {
|
||||
fallbackToExportServer();
|
||||
return;
|
||||
}
|
||||
|
||||
chart.getSVGForLocalExport(options, chartOptions, fallbackToExportServer, svgSuccess);
|
||||
};
|
||||
|
||||
// Extend the default options to use the local exporter logic
|
||||
merge(true, Highcharts.getOptions().exporting, {
|
||||
libURL: 'http://code.highcharts.com@product.cdnpath@/5.0.0/lib/',
|
||||
buttons: {
|
||||
contextButton: {
|
||||
menuItems: [{
|
||||
textKey: 'printChart',
|
||||
onclick: function() {
|
||||
this.print();
|
||||
}
|
||||
}, {
|
||||
separator: true
|
||||
}, {
|
||||
textKey: 'downloadPNG',
|
||||
onclick: function() {
|
||||
this.exportChartLocal();
|
||||
}
|
||||
}, {
|
||||
textKey: 'downloadJPEG',
|
||||
onclick: function() {
|
||||
this.exportChartLocal({
|
||||
type: 'image/jpeg'
|
||||
});
|
||||
}
|
||||
}, {
|
||||
textKey: 'downloadSVG',
|
||||
onclick: function() {
|
||||
this.exportChartLocal({
|
||||
type: 'image/svg+xml'
|
||||
});
|
||||
}
|
||||
}
|
||||
/*, {
|
||||
textKey: 'downloadPDF',
|
||||
onclick: function () {
|
||||
this.exportChartLocal({
|
||||
type: 'application/pdf'
|
||||
});
|
||||
}
|
||||
}*/
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
10
high/highcharts/js/modules/overlapping-datalabels.js
Normal file
10
high/highcharts/js/modules/overlapping-datalabels.js
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
|
||||
(c) 2009-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(b){"object"===typeof module&&module.exports?module.exports=b:b(Highcharts)})(function(b){(function(b){var g=b.Chart,h=b.each,r=b.pick,t=b.addEvent;g.prototype.callbacks.push(function(l){function b(){var d=[];h(l.series,function(a){var c=a.options.dataLabels,b=a.dataLabelCollections||["dataLabel"];(c.enabled||a._hasPointLabels)&&!c.allowOverlap&&a.visible&&h(b,function(c){h(a.points,function(a){a[c]&&(a[c].labelrank=r(a.labelrank,a.shapeArgs&&a.shapeArgs.height),d.push(a[c]))})})});l.hideOverlappingLabels(d)}
|
||||
b();t(l,"redraw",b)});g.prototype.hideOverlappingLabels=function(b){var m=b.length,d,a,c,e,k,n,p,q,f,g=function(a,b,c,d,e,f,g,h){return!(e>a+c||e+g<a||f>b+d||f+h<b)};for(a=0;a<m;a++)if(d=b[a])d.oldOpacity=d.opacity,d.newOpacity=1;b.sort(function(a,b){return(b.labelrank||0)-(a.labelrank||0)});for(a=0;a<m;a++)for(c=b[a],d=a+1;d<m;++d)if(e=b[d],c&&e&&c.placed&&e.placed&&0!==c.newOpacity&&0!==e.newOpacity&&(k=c.alignAttr,n=e.alignAttr,p=c.parentGroup,q=e.parentGroup,f=2*(c.box?0:c.padding),k=g(k.x+p.translateX,
|
||||
k.y+p.translateY,c.width-f,c.height-f,n.x+q.translateX,n.y+q.translateY,e.width-f,e.height-f)))(c.labelrank<e.labelrank?c:e).newOpacity=0;h(b,function(a){var b,c;a&&(c=a.newOpacity,a.oldOpacity!==c&&a.placed&&(c?a.show(!0):b=function(){a.hide()},a.alignAttr.opacity=c,a[a.isOld?"animate":"attr"](a.alignAttr,null,b)),a.isOld=!0)})}})(b)});
|
164
high/highcharts/js/modules/overlapping-datalabels.src.js
Normal file
164
high/highcharts/js/modules/overlapping-datalabels.src.js
Normal file
@ -0,0 +1,164 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
*
|
||||
* (c) 2009-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* (c) 2009-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
/**
|
||||
* Highcharts module to hide overlapping data labels. This module is included in Highcharts.
|
||||
*/
|
||||
var Chart = H.Chart,
|
||||
each = H.each,
|
||||
pick = H.pick,
|
||||
addEvent = H.addEvent;
|
||||
|
||||
// Collect potensial overlapping data labels. Stack labels probably don't need to be
|
||||
// considered because they are usually accompanied by data labels that lie inside the columns.
|
||||
Chart.prototype.callbacks.push(function(chart) {
|
||||
function collectAndHide() {
|
||||
var labels = [];
|
||||
|
||||
each(chart.series, function(series) {
|
||||
var dlOptions = series.options.dataLabels,
|
||||
collections = series.dataLabelCollections || ['dataLabel']; // Range series have two collections
|
||||
if ((dlOptions.enabled || series._hasPointLabels) && !dlOptions.allowOverlap && series.visible) { // #3866
|
||||
each(collections, function(coll) {
|
||||
each(series.points, function(point) {
|
||||
if (point[coll]) {
|
||||
point[coll].labelrank = pick(point.labelrank, point.shapeArgs && point.shapeArgs.height); // #4118
|
||||
labels.push(point[coll]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
chart.hideOverlappingLabels(labels);
|
||||
}
|
||||
|
||||
// Do it now ...
|
||||
collectAndHide();
|
||||
|
||||
// ... and after each chart redraw
|
||||
addEvent(chart, 'redraw', collectAndHide);
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Hide overlapping labels. Labels are moved and faded in and out on zoom to provide a smooth
|
||||
* visual imression.
|
||||
*/
|
||||
Chart.prototype.hideOverlappingLabels = function(labels) {
|
||||
|
||||
var len = labels.length,
|
||||
label,
|
||||
i,
|
||||
j,
|
||||
label1,
|
||||
label2,
|
||||
isIntersecting,
|
||||
pos1,
|
||||
pos2,
|
||||
parent1,
|
||||
parent2,
|
||||
padding,
|
||||
intersectRect = function(x1, y1, w1, h1, x2, y2, w2, h2) {
|
||||
return !(
|
||||
x2 > x1 + w1 ||
|
||||
x2 + w2 < x1 ||
|
||||
y2 > y1 + h1 ||
|
||||
y2 + h2 < y1
|
||||
);
|
||||
};
|
||||
|
||||
// Mark with initial opacity
|
||||
for (i = 0; i < len; i++) {
|
||||
label = labels[i];
|
||||
if (label) {
|
||||
label.oldOpacity = label.opacity;
|
||||
label.newOpacity = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent a situation in a gradually rising slope, that each label
|
||||
// will hide the previous one because the previous one always has
|
||||
// lower rank.
|
||||
labels.sort(function(a, b) {
|
||||
return (b.labelrank || 0) - (a.labelrank || 0);
|
||||
});
|
||||
|
||||
// Detect overlapping labels
|
||||
for (i = 0; i < len; i++) {
|
||||
label1 = labels[i];
|
||||
|
||||
for (j = i + 1; j < len; ++j) {
|
||||
label2 = labels[j];
|
||||
if (label1 && label2 && label1.placed && label2.placed && label1.newOpacity !== 0 && label2.newOpacity !== 0) {
|
||||
pos1 = label1.alignAttr;
|
||||
pos2 = label2.alignAttr;
|
||||
parent1 = label1.parentGroup; // Different panes have different positions
|
||||
parent2 = label2.parentGroup;
|
||||
padding = 2 * (label1.box ? 0 : label1.padding); // Substract the padding if no background or border (#4333)
|
||||
isIntersecting = intersectRect(
|
||||
pos1.x + parent1.translateX,
|
||||
pos1.y + parent1.translateY,
|
||||
label1.width - padding,
|
||||
label1.height - padding,
|
||||
pos2.x + parent2.translateX,
|
||||
pos2.y + parent2.translateY,
|
||||
label2.width - padding,
|
||||
label2.height - padding
|
||||
);
|
||||
|
||||
if (isIntersecting) {
|
||||
(label1.labelrank < label2.labelrank ? label1 : label2).newOpacity = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hide or show
|
||||
each(labels, function(label) {
|
||||
var complete,
|
||||
newOpacity;
|
||||
|
||||
if (label) {
|
||||
newOpacity = label.newOpacity;
|
||||
|
||||
if (label.oldOpacity !== newOpacity && label.placed) {
|
||||
|
||||
// Make sure the label is completely hidden to avoid catching clicks (#4362)
|
||||
if (newOpacity) {
|
||||
label.show(true);
|
||||
} else {
|
||||
complete = function() {
|
||||
label.hide();
|
||||
};
|
||||
}
|
||||
|
||||
// Animate or set the opacity
|
||||
label.alignAttr.opacity = newOpacity;
|
||||
label[label.isOld ? 'animate' : 'attr'](label.alignAttr, null, complete);
|
||||
|
||||
}
|
||||
label.isOld = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
18
high/highcharts/js/modules/series-label.js
Normal file
18
high/highcharts/js/modules/series-label.js
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
|
||||
(c) 2009-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(t){"object"===typeof module&&module.exports?module.exports=t:t(Highcharts)})(function(t){(function(u){function t(d,b,a,m,c,e){d=(e-b)*(a-d)-(m-b)*(c-d);return 0<d?!0:0>d?!1:!0}function v(d,b,a,m,c,e,f,l){return t(d,b,c,e,f,l)!==t(a,m,c,e,f,l)&&t(d,b,a,m,c,e)!==t(d,b,a,m,f,l)}function y(d,b,a,m,c,e,f,l){return v(d,b,d+a,b,c,e,f,l)||v(d+a,b,d+a,b+m,c,e,f,l)||v(d,b+m,d+a,b+m,c,e,f,l)||v(d,b,d,b+m,c,e,f,l)}function z(d){var b=this;d.call(b);clearTimeout(b.seriesLabelTimer);b.seriesLabelTimer=
|
||||
setTimeout(function(){b.boxesToAvoid=[];w(b.series,function(a){var d=a.options.label;d.enabled&&a.visible&&(a.graph||a.area)&&(a.interpolatedPoints=a.getPointsOnGraph(),w(d.boxesToAvoid||[],function(a){b.boxesToAvoid.push(a)}))});w(b.series,function(a){function d(a,b,c){return a>p&&a<=p+r-c.width&&b>=g&&b<=g+h-c.height}var c,e,f,l=[],k,n,p=(c=b.inverted)?a.yAxis.pos:a.xAxis.pos,g=c?a.xAxis.pos:a.yAxis.pos,r=b.inverted?a.yAxis.len:a.xAxis.len,h=b.inverted?a.xAxis.len:a.yAxis.len,q=a.interpolatedPoints;
|
||||
if(a.visible&&q){a.labelBySeries||(a.labelBySeries=b.renderer.label(a.name,0,-9999,"connector").css(D({color:a.color},a.options.label.styles)).attr({padding:0,opacity:0,stroke:a.color,"stroke-width":1}).add(a.group).animate({opacity:1},{duration:200}));c=a.labelBySeries.getBBox();c.width=Math.round(c.width);for(n=q.length-1;0<n;--n)e=q[n].chartX+3,f=q[n].chartY-c.height-3,d(e,f,c)&&(k=a.checkClearPoint(e,f,c)),k&&l.push(k),e=q[n].chartX+3,f=q[n].chartY+3,d(e,f,c)&&(k=a.checkClearPoint(e,f,c)),k&&
|
||||
l.push(k),e=q[n].chartX-c.width-3,f=q[n].chartY+3,d(e,f,c)&&(k=a.checkClearPoint(e,f,c)),k&&l.push(k),e=q[n].chartX-c.width-3,f=q[n].chartY-c.height-3,d(e,f,c)&&(k=a.checkClearPoint(e,f,c)),k&&l.push(k);if(!l.length)for(e=p+r-c.width;e>=p;e-=16)for(f=g;f<g+h-c.height;f+=16)(k=a.checkClearPoint(e,f,c,!0))&&l.push(k);l.length?(l.sort(function(a,b){return b.weight-a.weight}),k=l[0],b.boxesToAvoid.push({left:k.x,right:k.x+c.width,top:k.y,bottom:k.y+c.height}),Math.round(k.x)===Math.round(a.labelBySeries.x)&&
|
||||
Math.round(k.y)===Math.round(a.labelBySeries.y)||a.labelBySeries.attr({x:k.x-p,y:k.y-g,anchorX:k.connectorPoint&&k.connectorPoint.plotX,anchorY:k.connectorPoint&&k.connectorPoint.plotY,opacity:0}).animate({opacity:1})):a.labelBySeries&&(a.labelBySeries=a.labelBySeries.destroy())}})},350)}var A=u.wrap,w=u.each,D=u.extend,x=u.isNumber,B=u.Series,E=u.SVGRenderer,C=u.Chart;u.setOptions({plotOptions:{series:{label:{enabled:!0,connectorAllowed:!0,connectorNeighbourDistance:24,styles:{fontWeight:"bold"}}}}});
|
||||
E.prototype.symbols.connector=function(d,b,a,m,c){var e=c&&c.anchorX;c=c&&c.anchorY;var f,l,k=a/2;x(e)&&x(c)&&(f=["M",e,c],l=b-c,0>l&&(l=-m-l),l<a&&(k=e<d+a/2?l:a-l),c>b+m?f.push("L",d+k,b+m):c<b?f.push("L",d+k,b):e<d?f.push("L",d,b+m/2):e>d+a&&f.push("L",d+a,b+m/2));return f||[]};B.prototype.getPointsOnGraph=function(){var d=this.points,b,a,m=[],c,e,f,l;e=this.graph||this.area;f=e.element;var k=(b=this.chart.inverted)?this.yAxis.pos:this.xAxis.pos,n=b?this.xAxis.pos:this.yAxis.pos;if(this.getPointSpline&&
|
||||
f.getPointAtLength){e.toD&&(a=e.attr("d"),e.attr({d:e.toD}));l=f.getTotalLength();for(c=0;c<l;c+=16)b=f.getPointAtLength(c),m.push({chartX:k+b.x,chartY:n+b.y,plotX:b.x,plotY:b.y});a&&e.attr({d:a});b=d[d.length-1];b.chartX=k+b.plotX;b.chartY=n+b.plotY;m.push(b)}else for(l=d.length,c=0;c<l;c+=1){b=d[c];a=d[c-1];b.chartX=k+b.plotX;b.chartY=n+b.plotY;if(0<c&&(e=Math.abs(b.chartX-a.chartX),f=Math.abs(b.chartY-a.chartY),e=Math.max(e,f),16<e))for(e=Math.ceil(e/16),f=1;f<e;f+=1)m.push({chartX:a.chartX+f/
|
||||
e*(b.chartX-a.chartX),chartY:a.chartY+f/e*(b.chartY-a.chartY),plotX:a.plotX+f/e*(b.plotX-a.plotX),plotY:a.plotY+f/e*(b.plotY-a.plotY)});x(b.plotY)&&m.push(b)}return m};B.prototype.checkClearPoint=function(d,b,a,m){var c=Number.MAX_VALUE,e=Number.MAX_VALUE,f,l,k=this.options.label.connectorAllowed,n=this.chart,p,g,r,h;for(r=0;r<n.boxesToAvoid.length;r+=1){g=n.boxesToAvoid[r];h=d+a.width;p=b;var q=b+a.height;if(!(d>g.right||h<g.left||p>g.bottom||q<g.top))return!1}for(r=0;r<n.series.length;r+=1)if(p=
|
||||
n.series[r],g=p.interpolatedPoints,p.visible&&g){for(h=1;h<g.length;h+=1){if(y(d,b,a.width,a.height,g[h-1].chartX,g[h-1].chartY,g[h].chartX,g[h].chartY))return!1;this===p&&!f&&m&&(f=y(d-16,b-16,a.width+32,a.height+32,g[h-1].chartX,g[h-1].chartY,g[h].chartX,g[h].chartY));this!==p&&(c=Math.min(c,Math.pow(d+a.width/2-g[h].chartX,2)+Math.pow(b+a.height/2-g[h].chartY,2),Math.pow(d-g[h].chartX,2)+Math.pow(b-g[h].chartY,2),Math.pow(d+a.width-g[h].chartX,2)+Math.pow(b-g[h].chartY,2),Math.pow(d+a.width-g[h].chartX,
|
||||
2)+Math.pow(b+a.height-g[h].chartY,2),Math.pow(d-g[h].chartX,2)+Math.pow(b+a.height-g[h].chartY,2)))}if(k&&this===p&&(m&&!f||c<Math.pow(this.options.label.connectorNeighbourDistance,2))){for(h=1;h<g.length;h+=1)f=Math.min(Math.pow(d+a.width/2-g[h].chartX,2)+Math.pow(b+a.height/2-g[h].chartY,2),Math.pow(d-g[h].chartX,2)+Math.pow(b-g[h].chartY,2),Math.pow(d+a.width-g[h].chartX,2)+Math.pow(b-g[h].chartY,2),Math.pow(d+a.width-g[h].chartX,2)+Math.pow(b+a.height-g[h].chartY,2),Math.pow(d-g[h].chartX,2)+
|
||||
Math.pow(b+a.height-g[h].chartY,2)),f<e&&(e=f,l=g[h]);f=!0}}return!m||f?{x:d,y:b,weight:c-(l?e:0),connectorPoint:l}:!1};A(C.prototype,"render",z);A(C.prototype,"redraw",z)})(t)});
|
544
high/highcharts/js/modules/series-label.src.js
Normal file
544
high/highcharts/js/modules/series-label.src.js
Normal file
@ -0,0 +1,544 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
*
|
||||
* (c) 2009-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* (c) 2009-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
/**
|
||||
* EXPERIMENTAL Highcharts module to place labels next to a series in a natural position.
|
||||
*
|
||||
* TODO:
|
||||
* - add column support (box collision detection, boxesToAvoid logic)
|
||||
* - other series types, area etc.
|
||||
* - avoid data labels, when data labels above, show series label below.
|
||||
* - add more options (connector, format, formatter)
|
||||
*
|
||||
* http://jsfiddle.net/highcharts/L2u9rpwr/
|
||||
* http://jsfiddle.net/highcharts/y5A37/
|
||||
* http://jsfiddle.net/highcharts/264Nm/
|
||||
* http://jsfiddle.net/highcharts/y5A37/
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var labelDistance = 3,
|
||||
wrap = H.wrap,
|
||||
each = H.each,
|
||||
extend = H.extend,
|
||||
isNumber = H.isNumber,
|
||||
Series = H.Series,
|
||||
SVGRenderer = H.SVGRenderer,
|
||||
Chart = H.Chart;
|
||||
|
||||
H.setOptions({
|
||||
plotOptions: {
|
||||
series: {
|
||||
label: {
|
||||
enabled: true,
|
||||
// Allow labels to be placed distant to the graph if necessary, and
|
||||
// draw a connector line to the graph
|
||||
connectorAllowed: true,
|
||||
connectorNeighbourDistance: 24, // If the label is closer than this to a neighbour graph, draw a connector
|
||||
styles: {
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
// boxesToAvoid: []
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Counter-clockwise, part of the fast line intersection logic
|
||||
*/
|
||||
function ccw(x1, y1, x2, y2, x3, y3) {
|
||||
var cw = ((y3 - y1) * (x2 - x1)) - ((y2 - y1) * (x3 - x1));
|
||||
return cw > 0 ? true : cw < 0 ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if two lines intersect
|
||||
*/
|
||||
function intersectLine(x1, y1, x2, y2, x3, y3, x4, y4) {
|
||||
return ccw(x1, y1, x3, y3, x4, y4) !== ccw(x2, y2, x3, y3, x4, y4) &&
|
||||
ccw(x1, y1, x2, y2, x3, y3) !== ccw(x1, y1, x2, y2, x4, y4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if a box intersects with a line
|
||||
*/
|
||||
function boxIntersectLine(x, y, w, h, x1, y1, x2, y2) {
|
||||
return (
|
||||
intersectLine(x, y, x + w, y, x1, y1, x2, y2) || // top of label
|
||||
intersectLine(x + w, y, x + w, y + h, x1, y1, x2, y2) || // right of label
|
||||
intersectLine(x, y + h, x + w, y + h, x1, y1, x2, y2) || // bottom of label
|
||||
intersectLine(x, y, x, y + h, x1, y1, x2, y2) // left of label
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* General symbol definition for labels with connector
|
||||
*/
|
||||
SVGRenderer.prototype.symbols.connector = function(x, y, w, h, options) {
|
||||
var anchorX = options && options.anchorX,
|
||||
anchorY = options && options.anchorY,
|
||||
path,
|
||||
yOffset,
|
||||
lateral = w / 2;
|
||||
|
||||
if (isNumber(anchorX) && isNumber(anchorY)) {
|
||||
|
||||
path = ['M', anchorX, anchorY];
|
||||
|
||||
// Prefer 45 deg connectors
|
||||
yOffset = y - anchorY;
|
||||
if (yOffset < 0) {
|
||||
yOffset = -h - yOffset;
|
||||
}
|
||||
if (yOffset < w) {
|
||||
lateral = anchorX < x + (w / 2) ? yOffset : w - yOffset;
|
||||
}
|
||||
|
||||
// Anchor below label
|
||||
if (anchorY > y + h) {
|
||||
path.push('L', x + lateral, y + h);
|
||||
|
||||
// Anchor above label
|
||||
} else if (anchorY < y) {
|
||||
path.push('L', x + lateral, y);
|
||||
|
||||
// Anchor left of label
|
||||
} else if (anchorX < x) {
|
||||
path.push('L', x, y + h / 2);
|
||||
|
||||
// Anchor right of label
|
||||
} else if (anchorX > x + w) {
|
||||
path.push('L', x + w, y + h / 2);
|
||||
}
|
||||
}
|
||||
return path || [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Points to avoid. In addition to actual data points, the label should avoid
|
||||
* interpolated positions.
|
||||
*/
|
||||
Series.prototype.getPointsOnGraph = function() {
|
||||
var distance = 16,
|
||||
points = this.points,
|
||||
point,
|
||||
last,
|
||||
interpolated = [],
|
||||
i,
|
||||
deltaX,
|
||||
deltaY,
|
||||
delta,
|
||||
len,
|
||||
n,
|
||||
j,
|
||||
d,
|
||||
graph = this.graph || this.area,
|
||||
node = graph.element,
|
||||
inverted = this.chart.inverted,
|
||||
paneLeft = inverted ? this.yAxis.pos : this.xAxis.pos,
|
||||
paneTop = inverted ? this.xAxis.pos : this.yAxis.pos;
|
||||
|
||||
// For splines, get the point at length (possible caveat: peaks are not correctly detected)
|
||||
if (this.getPointSpline && node.getPointAtLength) {
|
||||
// If it is animating towards a path definition, use that briefly, and reset
|
||||
if (graph.toD) {
|
||||
d = graph.attr('d');
|
||||
graph.attr({
|
||||
d: graph.toD
|
||||
});
|
||||
}
|
||||
len = node.getTotalLength();
|
||||
for (i = 0; i < len; i += distance) {
|
||||
point = node.getPointAtLength(i);
|
||||
interpolated.push({
|
||||
chartX: paneLeft + point.x,
|
||||
chartY: paneTop + point.y,
|
||||
plotX: point.x,
|
||||
plotY: point.y
|
||||
});
|
||||
}
|
||||
if (d) {
|
||||
graph.attr({
|
||||
d: d
|
||||
});
|
||||
}
|
||||
// Last point
|
||||
point = points[points.length - 1];
|
||||
point.chartX = paneLeft + point.plotX;
|
||||
point.chartY = paneTop + point.plotY;
|
||||
interpolated.push(point);
|
||||
|
||||
// Interpolate
|
||||
} else {
|
||||
len = points.length;
|
||||
for (i = 0; i < len; i += 1) {
|
||||
|
||||
point = points[i];
|
||||
last = points[i - 1];
|
||||
|
||||
// Absolute coordinates so we can compare different panes
|
||||
point.chartX = paneLeft + point.plotX;
|
||||
point.chartY = paneTop + point.plotY;
|
||||
|
||||
// Add interpolated points
|
||||
if (i > 0) {
|
||||
deltaX = Math.abs(point.chartX - last.chartX);
|
||||
deltaY = Math.abs(point.chartY - last.chartY);
|
||||
delta = Math.max(deltaX, deltaY);
|
||||
if (delta > distance) {
|
||||
|
||||
n = Math.ceil(delta / distance);
|
||||
|
||||
for (j = 1; j < n; j += 1) {
|
||||
interpolated.push({
|
||||
chartX: last.chartX + (point.chartX - last.chartX) * (j / n),
|
||||
chartY: last.chartY + (point.chartY - last.chartY) * (j / n),
|
||||
plotX: last.plotX + (point.plotX - last.plotX) * (j / n),
|
||||
plotY: last.plotY + (point.plotY - last.plotY) * (j / n)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the real point in order to find positive and negative peaks
|
||||
if (isNumber(point.plotY)) {
|
||||
interpolated.push(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
return interpolated;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether a proposed label position is clear of other elements
|
||||
*/
|
||||
Series.prototype.checkClearPoint = function(x, y, bBox, checkDistance) {
|
||||
var distToOthersSquared = Number.MAX_VALUE, // distance to other graphs
|
||||
distToPointSquared = Number.MAX_VALUE,
|
||||
dist,
|
||||
connectorPoint,
|
||||
connectorEnabled = this.options.label.connectorAllowed,
|
||||
|
||||
chart = this.chart,
|
||||
series,
|
||||
points,
|
||||
leastDistance = 16,
|
||||
withinRange,
|
||||
i,
|
||||
j;
|
||||
|
||||
function intersectRect(r1, r2) {
|
||||
return !(r2.left > r1.right ||
|
||||
r2.right < r1.left ||
|
||||
r2.top > r1.bottom ||
|
||||
r2.bottom < r1.top);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weight in order to determine the ideal position. Larger distance to
|
||||
* other series gives more weight. Smaller distance to the actual point (connector points only)
|
||||
* gives more weight.
|
||||
*/
|
||||
function getWeight(distToOthersSquared, distToPointSquared) {
|
||||
return distToOthersSquared - distToPointSquared;
|
||||
}
|
||||
|
||||
// First check for collision with existing labels
|
||||
for (i = 0; i < chart.boxesToAvoid.length; i += 1) {
|
||||
if (intersectRect(chart.boxesToAvoid[i], {
|
||||
left: x,
|
||||
right: x + bBox.width,
|
||||
top: y,
|
||||
bottom: y + bBox.height
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// For each position, check if the lines around the label intersect with any of the
|
||||
// graphs
|
||||
for (i = 0; i < chart.series.length; i += 1) {
|
||||
series = chart.series[i];
|
||||
points = series.interpolatedPoints;
|
||||
if (series.visible && points) {
|
||||
for (j = 1; j < points.length; j += 1) {
|
||||
// If any of the box sides intersect with the line, return
|
||||
if (boxIntersectLine(
|
||||
x,
|
||||
y,
|
||||
bBox.width,
|
||||
bBox.height,
|
||||
points[j - 1].chartX,
|
||||
points[j - 1].chartY,
|
||||
points[j].chartX,
|
||||
points[j].chartY
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// But if it is too far away (a padded box doesn't intersect), also return
|
||||
if (this === series && !withinRange && checkDistance) {
|
||||
withinRange = boxIntersectLine(
|
||||
x - leastDistance,
|
||||
y - leastDistance,
|
||||
bBox.width + 2 * leastDistance,
|
||||
bBox.height + 2 * leastDistance,
|
||||
points[j - 1].chartX,
|
||||
points[j - 1].chartY,
|
||||
points[j].chartX,
|
||||
points[j].chartY
|
||||
);
|
||||
}
|
||||
|
||||
// Find the squared distance from the center of the label
|
||||
if (this !== series) {
|
||||
distToOthersSquared = Math.min(
|
||||
distToOthersSquared,
|
||||
Math.pow(x + bBox.width / 2 - points[j].chartX, 2) + Math.pow(y + bBox.height / 2 - points[j].chartY, 2),
|
||||
Math.pow(x - points[j].chartX, 2) + Math.pow(y - points[j].chartY, 2),
|
||||
Math.pow(x + bBox.width - points[j].chartX, 2) + Math.pow(y - points[j].chartY, 2),
|
||||
Math.pow(x + bBox.width - points[j].chartX, 2) + Math.pow(y + bBox.height - points[j].chartY, 2),
|
||||
Math.pow(x - points[j].chartX, 2) + Math.pow(y + bBox.height - points[j].chartY, 2)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Do we need a connector?
|
||||
if (connectorEnabled && this === series && ((checkDistance && !withinRange) ||
|
||||
distToOthersSquared < Math.pow(this.options.label.connectorNeighbourDistance, 2))) {
|
||||
for (j = 1; j < points.length; j += 1) {
|
||||
dist = Math.min(
|
||||
Math.pow(x + bBox.width / 2 - points[j].chartX, 2) + Math.pow(y + bBox.height / 2 - points[j].chartY, 2),
|
||||
Math.pow(x - points[j].chartX, 2) + Math.pow(y - points[j].chartY, 2),
|
||||
Math.pow(x + bBox.width - points[j].chartX, 2) + Math.pow(y - points[j].chartY, 2),
|
||||
Math.pow(x + bBox.width - points[j].chartX, 2) + Math.pow(y + bBox.height - points[j].chartY, 2),
|
||||
Math.pow(x - points[j].chartX, 2) + Math.pow(y + bBox.height - points[j].chartY, 2)
|
||||
);
|
||||
if (dist < distToPointSquared) {
|
||||
distToPointSquared = dist;
|
||||
connectorPoint = points[j];
|
||||
}
|
||||
}
|
||||
withinRange = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !checkDistance || withinRange ? {
|
||||
x: x,
|
||||
y: y,
|
||||
weight: getWeight(distToOthersSquared, connectorPoint ? distToPointSquared : 0),
|
||||
connectorPoint: connectorPoint
|
||||
} : false;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The main initiator method that runs on chart level after initiation and redraw. It runs in
|
||||
* a timeout to prevent locking, and loops over all series, taking all series and labels into
|
||||
* account when placing the labels.
|
||||
*/
|
||||
function drawLabels(proceed) {
|
||||
|
||||
var chart = this;
|
||||
|
||||
proceed.call(chart);
|
||||
|
||||
clearTimeout(chart.seriesLabelTimer);
|
||||
|
||||
chart.seriesLabelTimer = setTimeout(function() {
|
||||
|
||||
chart.boxesToAvoid = [];
|
||||
|
||||
// Build the interpolated points
|
||||
each(chart.series, function(series) {
|
||||
var options = series.options.label;
|
||||
if (options.enabled && series.visible && (series.graph || series.area)) {
|
||||
series.interpolatedPoints = series.getPointsOnGraph();
|
||||
|
||||
each(options.boxesToAvoid || [], function(box) {
|
||||
chart.boxesToAvoid.push(box);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
each(chart.series, function(series) {
|
||||
var bBox,
|
||||
x,
|
||||
y,
|
||||
results = [],
|
||||
clearPoint,
|
||||
i,
|
||||
best,
|
||||
inverted = chart.inverted,
|
||||
paneLeft = inverted ? series.yAxis.pos : series.xAxis.pos,
|
||||
paneTop = inverted ? series.xAxis.pos : series.yAxis.pos,
|
||||
paneWidth = chart.inverted ? series.yAxis.len : series.xAxis.len,
|
||||
paneHeight = chart.inverted ? series.xAxis.len : series.yAxis.len,
|
||||
points = series.interpolatedPoints;
|
||||
|
||||
function insidePane(x, y, bBox) {
|
||||
return x > paneLeft && x <= paneLeft + paneWidth - bBox.width &&
|
||||
y >= paneTop && y <= paneTop + paneHeight - bBox.height;
|
||||
}
|
||||
|
||||
if (series.visible && points) {
|
||||
|
||||
if (!series.labelBySeries) {
|
||||
series.labelBySeries = chart.renderer.label(series.name, 0, -9999, 'connector')
|
||||
.css(extend({
|
||||
color: series.color
|
||||
}, series.options.label.styles))
|
||||
.attr({
|
||||
padding: 0,
|
||||
opacity: 0,
|
||||
stroke: series.color,
|
||||
'stroke-width': 1
|
||||
})
|
||||
.add(series.group)
|
||||
.animate({
|
||||
opacity: 1
|
||||
}, {
|
||||
duration: 200
|
||||
});
|
||||
}
|
||||
|
||||
bBox = series.labelBySeries.getBBox();
|
||||
bBox.width = Math.round(bBox.width);
|
||||
|
||||
// Ideal positions are centered above or below a point on right side of chart
|
||||
for (i = points.length - 1; i > 0; i -= 1) {
|
||||
|
||||
// Right - up
|
||||
x = points[i].chartX + labelDistance;
|
||||
y = points[i].chartY - bBox.height - labelDistance;
|
||||
if (insidePane(x, y, bBox)) {
|
||||
best = series.checkClearPoint(
|
||||
x,
|
||||
y,
|
||||
bBox
|
||||
);
|
||||
}
|
||||
if (best) {
|
||||
results.push(best);
|
||||
}
|
||||
|
||||
// Right - down
|
||||
x = points[i].chartX + labelDistance;
|
||||
y = points[i].chartY + labelDistance;
|
||||
if (insidePane(x, y, bBox)) {
|
||||
best = series.checkClearPoint(
|
||||
x,
|
||||
y,
|
||||
bBox
|
||||
);
|
||||
}
|
||||
if (best) {
|
||||
results.push(best);
|
||||
}
|
||||
|
||||
// Left - down
|
||||
x = points[i].chartX - bBox.width - labelDistance;
|
||||
y = points[i].chartY + labelDistance;
|
||||
if (insidePane(x, y, bBox)) {
|
||||
best = series.checkClearPoint(
|
||||
x,
|
||||
y,
|
||||
bBox
|
||||
);
|
||||
}
|
||||
if (best) {
|
||||
results.push(best);
|
||||
}
|
||||
|
||||
// Left - up
|
||||
x = points[i].chartX - bBox.width - labelDistance;
|
||||
y = points[i].chartY - bBox.height - labelDistance;
|
||||
if (insidePane(x, y, bBox)) {
|
||||
best = series.checkClearPoint(
|
||||
x,
|
||||
y,
|
||||
bBox
|
||||
);
|
||||
}
|
||||
if (best) {
|
||||
results.push(best);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Brute force, try all positions on the chart in a 16x16 grid
|
||||
if (!results.length) {
|
||||
for (x = paneLeft + paneWidth - bBox.width; x >= paneLeft; x -= 16) {
|
||||
for (y = paneTop; y < paneTop + paneHeight - bBox.height; y += 16) {
|
||||
clearPoint = series.checkClearPoint(x, y, bBox, true);
|
||||
if (clearPoint) {
|
||||
results.push(clearPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (results.length) {
|
||||
|
||||
results.sort(function(a, b) {
|
||||
return b.weight - a.weight;
|
||||
});
|
||||
|
||||
best = results[0];
|
||||
|
||||
chart.boxesToAvoid.push({
|
||||
left: best.x,
|
||||
right: best.x + bBox.width,
|
||||
top: best.y,
|
||||
bottom: best.y + bBox.height
|
||||
});
|
||||
|
||||
// Move it if needed
|
||||
if (Math.round(best.x) !== Math.round(series.labelBySeries.x) || Math.round(best.y) !== Math.round(series.labelBySeries.y)) {
|
||||
series.labelBySeries
|
||||
.attr({
|
||||
x: best.x - paneLeft,
|
||||
y: best.y - paneTop,
|
||||
anchorX: best.connectorPoint && best.connectorPoint.plotX,
|
||||
anchorY: best.connectorPoint && best.connectorPoint.plotY,
|
||||
opacity: 0
|
||||
})
|
||||
.animate({
|
||||
opacity: 1
|
||||
});
|
||||
}
|
||||
|
||||
} else if (series.labelBySeries) {
|
||||
series.labelBySeries = series.labelBySeries.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 350);
|
||||
|
||||
}
|
||||
wrap(Chart.prototype, 'render', drawLabels);
|
||||
wrap(Chart.prototype, 'redraw', drawLabels);
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
14
high/highcharts/js/modules/solid-gauge.js
Normal file
14
high/highcharts/js/modules/solid-gauge.js
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
Solid angular gauge module
|
||||
|
||||
(c) 2010-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(f){"object"===typeof module&&module.exports?module.exports=f:f(Highcharts)})(function(f){(function(b){var f=b.pInt,u=b.pick,m=b.each,v=b.isNumber,n;n={initDataClasses:function(a){var c=this,h=this.chart,e,t=0,l=this.options;this.dataClasses=e=[];m(a.dataClasses,function(g,d){var f;g=b.merge(g);e.push(g);g.color||("category"===l.dataClassColor?(f=h.options.colors,g.color=f[t++],t===f.length&&(t=0)):g.color=c.tweenColors(b.color(l.minColor),b.color(l.maxColor),d/(a.dataClasses.length-1)))})},
|
||||
initStops:function(a){this.stops=a.stops||[[0,this.options.minColor],[1,this.options.maxColor]];m(this.stops,function(a){a.color=b.color(a[1])})},toColor:function(a,c){var h,e=this.stops,b,f=this.dataClasses,g,d;if(f)for(d=f.length;d--;){if(g=f[d],b=g.from,e=g.to,(void 0===b||a>=b)&&(void 0===e||a<=e)){h=g.color;c&&(c.dataClass=d);break}}else{this.isLog&&(a=this.val2lin(a));h=1-(this.max-a)/(this.max-this.min);for(d=e.length;d--&&!(h>e[d][0]););b=e[d]||e[d+1];e=e[d+1]||b;h=1-(e[0]-h)/(e[0]-b[0]||
|
||||
1);h=this.tweenColors(b.color,e.color,h)}return h},tweenColors:function(a,c,b){var e;c.rgba.length&&a.rgba.length?(a=a.rgba,c=c.rgba,e=1!==c[3]||1!==a[3],a=(e?"rgba(":"rgb(")+Math.round(c[0]+(a[0]-c[0])*(1-b))+","+Math.round(c[1]+(a[1]-c[1])*(1-b))+","+Math.round(c[2]+(a[2]-c[2])*(1-b))+(e?","+(c[3]+(a[3]-c[3])*(1-b)):"")+")"):a=c.input||"none";return a}};m(["fill","stroke"],function(a){b.Fx.prototype[a+"Setter"]=function(){this.elem.attr(a,n.tweenColors(b.color(this.start),b.color(this.end),this.pos))}});
|
||||
b.seriesType("solidgauge","gauge",{colorByPoint:!0},{bindAxes:function(){var a;b.seriesTypes.gauge.prototype.bindAxes.call(this);a=this.yAxis;b.extend(a,n);a.options.dataClasses&&a.initDataClasses(a.options);a.initStops(a.options)},drawPoints:function(){var a=this,c=a.yAxis,b=c.center,e=a.options,t=a.chart.renderer,l=e.overshoot,g=v(l)?l/180*Math.PI:0;m(a.points,function(d){var l=d.graphic,k=c.startAngleRad+c.translate(d.y,null,null,null,!0),m=f(u(d.options.radius,e.radius,100))*b[2]/200,p=f(u(d.options.innerRadius,
|
||||
e.innerRadius,60))*b[2]/200,q=c.toColor(d.y,d),r=Math.min(c.startAngleRad,c.endAngleRad),n=Math.max(c.startAngleRad,c.endAngleRad);"none"===q&&(q=d.color||a.color||"none");"none"!==q&&(d.color=q);k=Math.max(r-g,Math.min(n+g,k));!1===e.wrap&&(k=Math.max(r,Math.min(n,k)));r=Math.min(k,c.startAngleRad);k=Math.max(k,c.startAngleRad);k-r>2*Math.PI&&(k=r+2*Math.PI);d.shapeArgs=p={x:b[0],y:b[1],r:m,innerR:p,start:r,end:k,fill:q};d.startR=m;l?(d=p.d,l.animate(p),d&&(p.d=d)):(d.graphic=t.arc(p).addClass("highcharts-point").attr({fill:q,
|
||||
"sweep-flag":0}).add(a.group),"square"!==e.linecap&&d.graphic.attr({"stroke-linecap":"round","stroke-linejoin":"round"}),d.graphic.attr({stroke:e.borderColor||"none","stroke-width":e.borderWidth||0}))})},animate:function(a){a||(this.startAngleRad=this.yAxis.startAngleRad,b.seriesTypes.pie.prototype.animate.call(this,a))}})})(f)});
|
287
high/highcharts/js/modules/solid-gauge.src.js
Normal file
287
high/highcharts/js/modules/solid-gauge.src.js
Normal file
@ -0,0 +1,287 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
* Solid angular gauge module
|
||||
*
|
||||
* (c) 2010-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* Solid angular gauge module
|
||||
*
|
||||
* (c) 2010-2016 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
var pInt = H.pInt,
|
||||
pick = H.pick,
|
||||
each = H.each,
|
||||
isNumber = H.isNumber,
|
||||
colorAxisMethods;
|
||||
|
||||
// These methods are defined in the ColorAxis object, and copied here.
|
||||
// If we implement an AMD system we should make ColorAxis a dependency.
|
||||
colorAxisMethods = {
|
||||
|
||||
|
||||
initDataClasses: function(userOptions) {
|
||||
var axis = this,
|
||||
chart = this.chart,
|
||||
dataClasses,
|
||||
colorCounter = 0,
|
||||
options = this.options;
|
||||
this.dataClasses = dataClasses = [];
|
||||
|
||||
each(userOptions.dataClasses, function(dataClass, i) {
|
||||
var colors;
|
||||
|
||||
dataClass = H.merge(dataClass);
|
||||
dataClasses.push(dataClass);
|
||||
if (!dataClass.color) {
|
||||
if (options.dataClassColor === 'category') {
|
||||
colors = chart.options.colors;
|
||||
dataClass.color = colors[colorCounter++];
|
||||
// loop back to zero
|
||||
if (colorCounter === colors.length) {
|
||||
colorCounter = 0;
|
||||
}
|
||||
} else {
|
||||
dataClass.color = axis.tweenColors(H.color(options.minColor), H.color(options.maxColor), i / (userOptions.dataClasses.length - 1));
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
initStops: function(userOptions) {
|
||||
this.stops = userOptions.stops || [
|
||||
[0, this.options.minColor],
|
||||
[1, this.options.maxColor]
|
||||
];
|
||||
each(this.stops, function(stop) {
|
||||
stop.color = H.color(stop[1]);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Translate from a value to a color
|
||||
*/
|
||||
toColor: function(value, point) {
|
||||
var pos,
|
||||
stops = this.stops,
|
||||
from,
|
||||
to,
|
||||
color,
|
||||
dataClasses = this.dataClasses,
|
||||
dataClass,
|
||||
i;
|
||||
|
||||
if (dataClasses) {
|
||||
i = dataClasses.length;
|
||||
while (i--) {
|
||||
dataClass = dataClasses[i];
|
||||
from = dataClass.from;
|
||||
to = dataClass.to;
|
||||
if ((from === undefined || value >= from) && (to === undefined || value <= to)) {
|
||||
color = dataClass.color;
|
||||
if (point) {
|
||||
point.dataClass = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (this.isLog) {
|
||||
value = this.val2lin(value);
|
||||
}
|
||||
pos = 1 - ((this.max - value) / (this.max - this.min));
|
||||
i = stops.length;
|
||||
while (i--) {
|
||||
if (pos > stops[i][0]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
from = stops[i] || stops[i + 1];
|
||||
to = stops[i + 1] || from;
|
||||
|
||||
// The position within the gradient
|
||||
pos = 1 - (to[0] - pos) / ((to[0] - from[0]) || 1);
|
||||
|
||||
color = this.tweenColors(
|
||||
from.color,
|
||||
to.color,
|
||||
pos
|
||||
);
|
||||
}
|
||||
return color;
|
||||
},
|
||||
/*
|
||||
* Return an intermediate color between two colors, according to pos where 0
|
||||
* is the from color and 1 is the to color.
|
||||
*/
|
||||
tweenColors: function(from, to, pos) {
|
||||
// Check for has alpha, because rgba colors perform worse due to lack of
|
||||
// support in WebKit.
|
||||
var hasAlpha,
|
||||
ret;
|
||||
|
||||
// Unsupported color, return to-color (#3920)
|
||||
if (!to.rgba.length || !from.rgba.length) {
|
||||
ret = to.input || 'none';
|
||||
|
||||
// Interpolate
|
||||
} else {
|
||||
from = from.rgba;
|
||||
to = to.rgba;
|
||||
hasAlpha = (to[3] !== 1 || from[3] !== 1);
|
||||
ret = (hasAlpha ? 'rgba(' : 'rgb(') +
|
||||
Math.round(to[0] + (from[0] - to[0]) * (1 - pos)) + ',' +
|
||||
Math.round(to[1] + (from[1] - to[1]) * (1 - pos)) + ',' +
|
||||
Math.round(to[2] + (from[2] - to[2]) * (1 - pos)) +
|
||||
(hasAlpha ? (',' + (to[3] + (from[3] - to[3]) * (1 - pos))) : '') + ')';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle animation of the color attributes directly
|
||||
*/
|
||||
each(['fill', 'stroke'], function(prop) {
|
||||
H.Fx.prototype[prop + 'Setter'] = function() {
|
||||
this.elem.attr(prop, colorAxisMethods.tweenColors(H.color(this.start), H.color(this.end), this.pos));
|
||||
};
|
||||
});
|
||||
|
||||
// The solidgauge series type
|
||||
H.seriesType('solidgauge', 'gauge', {
|
||||
colorByPoint: true
|
||||
|
||||
}, {
|
||||
bindAxes: function() {
|
||||
var axis;
|
||||
H.seriesTypes.gauge.prototype.bindAxes.call(this);
|
||||
|
||||
axis = this.yAxis;
|
||||
H.extend(axis, colorAxisMethods);
|
||||
|
||||
// Prepare data classes
|
||||
if (axis.options.dataClasses) {
|
||||
axis.initDataClasses(axis.options);
|
||||
}
|
||||
axis.initStops(axis.options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Draw the points where each point is one needle
|
||||
*/
|
||||
drawPoints: function() {
|
||||
var series = this,
|
||||
yAxis = series.yAxis,
|
||||
center = yAxis.center,
|
||||
options = series.options,
|
||||
renderer = series.chart.renderer,
|
||||
overshoot = options.overshoot,
|
||||
overshootVal = isNumber(overshoot) ? overshoot / 180 * Math.PI : 0;
|
||||
|
||||
each(series.points, function(point) {
|
||||
var graphic = point.graphic,
|
||||
rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true),
|
||||
radius = (pInt(pick(point.options.radius, options.radius, 100)) * center[2]) / 200,
|
||||
innerRadius = (pInt(pick(point.options.innerRadius, options.innerRadius, 60)) * center[2]) / 200,
|
||||
shapeArgs,
|
||||
d,
|
||||
toColor = yAxis.toColor(point.y, point),
|
||||
axisMinAngle = Math.min(yAxis.startAngleRad, yAxis.endAngleRad),
|
||||
axisMaxAngle = Math.max(yAxis.startAngleRad, yAxis.endAngleRad),
|
||||
minAngle,
|
||||
maxAngle;
|
||||
|
||||
if (toColor === 'none') { // #3708
|
||||
toColor = point.color || series.color || 'none';
|
||||
}
|
||||
if (toColor !== 'none') {
|
||||
point.color = toColor;
|
||||
}
|
||||
|
||||
// Handle overshoot and clipping to axis max/min
|
||||
rotation = Math.max(axisMinAngle - overshootVal, Math.min(axisMaxAngle + overshootVal, rotation));
|
||||
|
||||
// Handle the wrap option
|
||||
if (options.wrap === false) {
|
||||
rotation = Math.max(axisMinAngle, Math.min(axisMaxAngle, rotation));
|
||||
}
|
||||
|
||||
minAngle = Math.min(rotation, yAxis.startAngleRad);
|
||||
maxAngle = Math.max(rotation, yAxis.startAngleRad);
|
||||
|
||||
if (maxAngle - minAngle > 2 * Math.PI) {
|
||||
maxAngle = minAngle + 2 * Math.PI;
|
||||
}
|
||||
|
||||
point.shapeArgs = shapeArgs = {
|
||||
x: center[0],
|
||||
y: center[1],
|
||||
r: radius,
|
||||
innerR: innerRadius,
|
||||
start: minAngle,
|
||||
end: maxAngle,
|
||||
fill: toColor
|
||||
};
|
||||
point.startR = radius; // For PieSeries.animate
|
||||
|
||||
if (graphic) {
|
||||
d = shapeArgs.d;
|
||||
graphic.animate(shapeArgs);
|
||||
if (d) {
|
||||
shapeArgs.d = d; // animate alters it
|
||||
}
|
||||
} else {
|
||||
point.graphic = renderer.arc(shapeArgs)
|
||||
.addClass('highcharts-point')
|
||||
.attr({
|
||||
fill: toColor,
|
||||
'sweep-flag': 0
|
||||
})
|
||||
.add(series.group);
|
||||
|
||||
|
||||
if (options.linecap !== 'square') {
|
||||
point.graphic.attr({
|
||||
'stroke-linecap': 'round',
|
||||
'stroke-linejoin': 'round'
|
||||
});
|
||||
}
|
||||
point.graphic.attr({
|
||||
stroke: options.borderColor || 'none',
|
||||
'stroke-width': options.borderWidth || 0
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Extend the pie slice animation by animating from start angle and up
|
||||
*/
|
||||
animate: function(init) {
|
||||
|
||||
if (!init) {
|
||||
this.startAngleRad = this.yAxis.startAngleRad;
|
||||
H.seriesTypes.pie.prototype.animate.call(this, init);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
31
high/highcharts/js/modules/treemap.js
Normal file
31
high/highcharts/js/modules/treemap.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
Highcharts JS v5.0.0 (2016-09-29)
|
||||
|
||||
(c) 2014 Highsoft AS
|
||||
Authors: Jon Arild Nygard / Oystein Moseng
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(t){"object"===typeof module&&module.exports?module.exports=t:t(Highcharts)})(function(t){(function(g){var t=g.seriesType,n=g.seriesTypes,E=g.map,v=g.merge,y=g.extend,z=g.noop,m=g.each,x=g.grep,k=g.pick,u=g.Series,F=g.stableSort,A=g.Color,G=function(a,b,c){var d;c=c||this;for(d in a)a.hasOwnProperty(d)&&b.call(c,a[d],d,a)},B=function(a,b,c,d){d=d||this;a=a||[];m(a,function(e,f){c=b.call(d,c,e,f,a)});return c},w=function(a,b,c){c=c||this;a=b.call(c,a);!1!==a&&w(a,b,c)};t("treemap","scatter",
|
||||
{showInLegend:!1,marker:!1,dataLabels:{enabled:!0,defer:!1,verticalAlign:"middle",formatter:function(){return this.point.name||this.point.id},inside:!0},tooltip:{headerFormat:"",pointFormat:"<b>{point.name}</b>: {point.value}</b><br/>"},layoutAlgorithm:"sliceAndDice",layoutStartingDirection:"vertical",alternateStartingDirection:!1,levelIsConstant:!0,drillUpButton:{position:{align:"right",x:-10,y:10}},borderColor:"#e6e6e6",borderWidth:1,opacity:.15,states:{hover:{borderColor:"#999999",brightness:n.heatmap?
|
||||
0:.1,opacity:.75,shadow:!1}}},{pointArrayMap:["value"],axisTypes:n.heatmap?["xAxis","yAxis","colorAxis"]:["xAxis","yAxis"],optionalAxis:"colorAxis",getSymbol:z,parallelArrays:["x","y","value","colorValue"],colorKey:"colorValue",translateColors:n.heatmap&&n.heatmap.prototype.translateColors,trackerGroups:["group","dataLabelsGroup"],getListOfParents:function(a,b){var c=B(a,function(a,c,b){c=k(c.parent,"");void 0===a[c]&&(a[c]=[]);a[c].push(b);return a},{});G(c,function(a,c,f){""!==c&&-1===g.inArray(c,
|
||||
b)&&(m(a,function(a){f[""].push(a)}),delete f[c])});return c},getTree:function(){var a,b=this;a=E(this.data,function(a){return a.id});a=b.getListOfParents(this.data,a);b.nodeMap=[];a=b.buildNode("",-1,0,a,null);w(this.nodeMap[this.rootNode],function(a){var d=!1,e=a.parent;a.visible=!0;if(e||""===e)d=b.nodeMap[e];return d});w(this.nodeMap[this.rootNode].children,function(a){var b=!1;m(a,function(a){a.visible=!0;a.children.length&&(b=(b||[]).concat(a.children))});return b});this.setTreeValues(a);return a},
|
||||
init:function(a,b){u.prototype.init.call(this,a,b);this.options.allowDrillToNode&&this.drillTo()},buildNode:function(a,b,c,d,e){var f=this,h=[],C=f.points[b],D;m(d[a]||[],function(b){D=f.buildNode(f.points[b].id,b,c+1,d,a);h.push(D)});b={id:a,i:b,children:h,level:c,parent:e,visible:!1};f.nodeMap[b.id]=b;C&&(C.node=b);return b},setTreeValues:function(a){var b=this,c=b.options,d=0,e=[],f,h=b.points[a.i];m(a.children,function(a){a=b.setTreeValues(a);e.push(a);a.ignore?w(a.children,function(a){var c=
|
||||
!1;m(a,function(a){y(a,{ignore:!0,isLeaf:!1,visible:!1});a.children.length&&(c=(c||[]).concat(a.children))});return c}):d+=a.val});F(e,function(a,c){return a.sortIndex-c.sortIndex});f=k(h&&h.options.value,d);h&&(h.value=f);y(a,{children:e,childrenTotal:d,ignore:!(k(h&&h.visible,!0)&&0<f),isLeaf:a.visible&&!d,levelDynamic:c.levelIsConstant?a.level:a.level-b.nodeMap[b.rootNode].level,name:k(h&&h.name,""),sortIndex:k(h&&h.sortIndex,-f),val:f});return a},calculateChildrenAreas:function(a,b){var c=this,
|
||||
d=c.options,e=this.levelMap[a.levelDynamic+1],f=k(c[e&&e.layoutAlgorithm]&&e.layoutAlgorithm,d.layoutAlgorithm),h=d.alternateStartingDirection,g=[],d=x(a.children,function(a){return!a.ignore});e&&e.layoutStartingDirection&&(b.direction="vertical"===e.layoutStartingDirection?0:1);g=c[f](b,d);m(d,function(a,e){var d=g[e];a.values=v(d,{val:a.childrenTotal,direction:h?1-b.direction:b.direction});a.pointValues=v(d,{x:d.x/c.axisRatio,width:d.width/c.axisRatio});a.children.length&&c.calculateChildrenAreas(a,
|
||||
a.values)})},setPointValues:function(){var a=this.xAxis,b=this.yAxis;m(this.points,function(c){var d=c.node,e=d.pointValues,f,h;e&&d.visible?(d=Math.round(a.translate(e.x,0,0,0,1))-.5,f=Math.round(a.translate(e.x+e.width,0,0,0,1))-.5,h=Math.round(b.translate(e.y,0,0,0,1))-.5,e=Math.round(b.translate(e.y+e.height,0,0,0,1))-.5,c.shapeType="rect",c.shapeArgs={x:Math.min(d,f),y:Math.min(h,e),width:Math.abs(f-d),height:Math.abs(e-h)},c.plotX=c.shapeArgs.x+c.shapeArgs.width/2,c.plotY=c.shapeArgs.y+c.shapeArgs.height/
|
||||
2):(delete c.plotX,delete c.plotY)})},setColorRecursive:function(a,b,c){var d=this,e,f;a&&(e=d.points[a.i],f=d.levelMap[a.levelDynamic],b=k(e&&e.options.color,f&&f.color,b),c=k(e&&e.options.colorIndex,f&&f.colorIndex,c),e&&(e.color=b,e.colorIndex=c),a.children.length&&m(a.children,function(a){d.setColorRecursive(a,b,c)}))},algorithmGroup:function(a,b,c,d){this.height=a;this.width=b;this.plot=d;this.startDirection=this.direction=c;this.lH=this.nH=this.lW=this.nW=this.total=0;this.elArr=[];this.lP=
|
||||
{total:0,lH:0,nH:0,lW:0,nW:0,nR:0,lR:0,aspectRatio:function(a,c){return Math.max(a/c,c/a)}};this.addElement=function(a){this.lP.total=this.elArr[this.elArr.length-1];this.total+=a;0===this.direction?(this.lW=this.nW,this.lP.lH=this.lP.total/this.lW,this.lP.lR=this.lP.aspectRatio(this.lW,this.lP.lH),this.nW=this.total/this.height,this.lP.nH=this.lP.total/this.nW,this.lP.nR=this.lP.aspectRatio(this.nW,this.lP.nH)):(this.lH=this.nH,this.lP.lW=this.lP.total/this.lH,this.lP.lR=this.lP.aspectRatio(this.lP.lW,
|
||||
this.lH),this.nH=this.total/this.width,this.lP.nW=this.lP.total/this.nH,this.lP.nR=this.lP.aspectRatio(this.lP.nW,this.nH));this.elArr.push(a)};this.reset=function(){this.lW=this.nW=0;this.elArr=[];this.total=0}},algorithmCalcPoints:function(a,b,c,d){var e,f,h,g,k=c.lW,p=c.lH,l=c.plot,n,q=0,r=c.elArr.length-1;b?(k=c.nW,p=c.nH):n=c.elArr[c.elArr.length-1];m(c.elArr,function(a){if(b||q<r)0===c.direction?(e=l.x,f=l.y,h=k,g=a/h):(e=l.x,f=l.y,g=p,h=a/g),d.push({x:e,y:f,width:h,height:g}),0===c.direction?
|
||||
l.y+=g:l.x+=h;q+=1});c.reset();0===c.direction?c.width-=k:c.height-=p;l.y=l.parent.y+(l.parent.height-c.height);l.x=l.parent.x+(l.parent.width-c.width);a&&(c.direction=1-c.direction);b||c.addElement(n)},algorithmLowAspectRatio:function(a,b,c){var d=[],e=this,f,h={x:b.x,y:b.y,parent:b},g=0,k=c.length-1,p=new this.algorithmGroup(b.height,b.width,b.direction,h);m(c,function(c){f=c.val/b.val*b.height*b.width;p.addElement(f);p.lP.nR>p.lP.lR&&e.algorithmCalcPoints(a,!1,p,d,h);g===k&&e.algorithmCalcPoints(a,
|
||||
!0,p,d,h);g+=1});return d},algorithmFill:function(a,b,c){var d=[],e,f=b.direction,h=b.x,g=b.y,k=b.width,p=b.height,l,n,q,r;m(c,function(c){e=c.val/b.val*b.height*b.width;l=h;n=g;0===f?(r=p,q=e/r,k-=q,h+=q):(q=k,r=e/q,p-=r,g+=r);d.push({x:l,y:n,width:q,height:r});a&&(f=1-f)});return d},strip:function(a,b){return this.algorithmLowAspectRatio(!1,a,b)},squarified:function(a,b){return this.algorithmLowAspectRatio(!0,a,b)},sliceAndDice:function(a,b){return this.algorithmFill(!0,a,b)},stripes:function(a,
|
||||
b){return this.algorithmFill(!1,a,b)},translate:function(){var a,b;u.prototype.translate.call(this);this.rootNode=k(this.options.rootId,"");this.levelMap=B(this.options.levels,function(a,b){a[b.level]=b;return a},{});b=this.tree=this.getTree();this.axisRatio=this.xAxis.len/this.yAxis.len;this.nodeMap[""].pointValues=a={x:0,y:0,width:100,height:100};this.nodeMap[""].values=a=v(a,{width:a.width*this.axisRatio,direction:"vertical"===this.options.layoutStartingDirection?0:1,val:b.val});this.calculateChildrenAreas(b,
|
||||
a);this.colorAxis?this.translateColors():this.options.colorByPoint||this.setColorRecursive(this.tree);this.options.allowDrillToNode&&(b=this.nodeMap[this.rootNode].pointValues,this.xAxis.setExtremes(b.x,b.x+b.width,!1),this.yAxis.setExtremes(b.y,b.y+b.height,!1),this.xAxis.setScale(),this.yAxis.setScale());this.setPointValues()},drawDataLabels:function(){var a=this,b=x(a.points,function(a){return a.node.visible}),c,d;m(b,function(b){d=a.levelMap[b.node.levelDynamic];c={style:{}};b.node.isLeaf||(c.enabled=
|
||||
!1);d&&d.dataLabels&&(c=v(c,d.dataLabels),a._hasPointLabels=!0);b.shapeArgs&&(c.style.width=b.shapeArgs.width,b.dataLabel&&b.dataLabel.css({width:b.shapeArgs.width+"px"}));b.dlOptions=v(c,b.options.dataLabels)});u.prototype.drawDataLabels.call(this)},alignDataLabel:function(a){n.column.prototype.alignDataLabel.apply(this,arguments);a.dataLabel&&a.dataLabel.attr({zIndex:a.node.zIndex+1})},pointAttribs:function(a,b){var c=this.levelMap[a.node.levelDynamic]||{},d=this.options,e=b&&d.states[b]||{},f=
|
||||
a.getClassName(),c={stroke:a.borderColor||c.borderColor||e.borderColor||d.borderColor,"stroke-width":k(a.borderWidth,c.borderWidth,e.borderWidth,d.borderWidth),dashstyle:a.borderDashStyle||c.borderDashStyle||e.borderDashStyle||d.borderDashStyle,fill:a.color||this.color};-1!==f.indexOf("highcharts-above-level")?(c.fill="none",c["stroke-width"]=0):-1!==f.indexOf("highcharts-internal-node-interactive")?(d=k(e.opacity,d.opacity),c.fill=A(c.fill).setOpacity(d).get(),c.cursor="pointer"):-1!==f.indexOf("highcharts-internal-node")?
|
||||
c.fill="none":b&&(c.fill=A(c.fill).brighten(e.brightness).get());return c},drawPoints:function(){var a=this,b=x(a.points,function(a){return a.node.visible});m(b,function(c){var b="levelGroup-"+c.node.levelDynamic;a[b]||(a[b]=a.chart.renderer.g(b).attr({zIndex:1E3-c.node.levelDynamic}).add(a.group));c.group=a[b]});n.column.prototype.drawPoints.call(this);a.options.allowDrillToNode&&m(b,function(b){b.graphic&&(b.drillId=a.options.interactByLeaf?a.drillToByLeaf(b):a.drillToByGroup(b))})},drillTo:function(){var a=
|
||||
this;g.addEvent(a,"click",function(b){b=b.point;var c=b.drillId,d;c&&(d=a.nodeMap[a.rootNode].name||a.rootNode,b.setState(""),a.drillToNode(c),a.showDrillUpButton(d))})},drillToByGroup:function(a){var b=!1;1!==a.node.level-this.nodeMap[this.rootNode].level||a.node.isLeaf||(b=a.id);return b},drillToByLeaf:function(a){var b=!1;if(a.node.parent!==this.rootNode&&a.node.isLeaf)for(a=a.node;!b;)a=this.nodeMap[a.parent],a.parent===this.rootNode&&(b=a.id);return b},drillUp:function(){var a=null;this.rootNode&&
|
||||
(a=this.nodeMap[this.rootNode],a=null!==a.parent?this.nodeMap[a.parent]:this.nodeMap[""]);null!==a&&(this.drillToNode(a.id),""===a.id?this.drillUpButton=this.drillUpButton.destroy():(a=this.nodeMap[a.parent],this.showDrillUpButton(a.name||a.id)))},drillToNode:function(a){this.options.rootId=a;this.isDirty=!0;this.chart.redraw()},showDrillUpButton:function(a){var b=this;a=a||"< Back";var c=b.options.drillUpButton,d,e;c.text&&(a=c.text);this.drillUpButton?this.drillUpButton.attr({text:a}).align():(e=
|
||||
(d=c.theme)&&d.states,this.drillUpButton=this.chart.renderer.button(a,null,null,function(){b.drillUp()},d,e&&e.hover,e&&e.select).attr({align:c.position.align,zIndex:7}).add().align(c.position,!1,c.relativeTo||"plotBox"))},buildKDTree:z,drawLegendSymbol:g.LegendSymbolMixin.drawRectangle,getExtremes:function(){u.prototype.getExtremes.call(this,this.colorValueData);this.valueMin=this.dataMin;this.valueMax=this.dataMax;u.prototype.getExtremes.call(this)},getExtremesFromAll:!0,bindAxes:function(){var a=
|
||||
{endOnTick:!1,gridLineWidth:0,lineWidth:0,min:0,dataMin:0,minPadding:0,max:100,dataMax:100,maxPadding:0,startOnTick:!1,title:null,tickPositions:[]};u.prototype.bindAxes.call(this);g.extend(this.yAxis.options,a);g.extend(this.xAxis.options,a)}},{getClassName:function(){var a=g.Point.prototype.getClassName.call(this),b=this.series,c=b.options;this.node.level<=b.nodeMap[b.rootNode].level?a+=" highcharts-above-level":this.node.isLeaf||k(c.interactByLeaf,!c.allowDrillToNode)?this.node.isLeaf||(a+=" highcharts-internal-node"):
|
||||
a+=" highcharts-internal-node-interactive";return a},setState:function(a){g.Point.prototype.setState.call(this,a);this.graphic.attr({zIndex:"hover"===a?1:0})},setVisible:n.pie.prototype.pointClass.prototype.setVisible})})(t)});
|
930
high/highcharts/js/modules/treemap.src.js
Normal file
930
high/highcharts/js/modules/treemap.src.js
Normal file
@ -0,0 +1,930 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.0 (2016-09-29)
|
||||
*
|
||||
* (c) 2014 Highsoft AS
|
||||
* Authors: Jon Arild Nygard / Oystein Moseng
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* (c) 2014 Highsoft AS
|
||||
* Authors: Jon Arild Nygard / Oystein Moseng
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
var seriesType = H.seriesType,
|
||||
seriesTypes = H.seriesTypes,
|
||||
map = H.map,
|
||||
merge = H.merge,
|
||||
extend = H.extend,
|
||||
noop = H.noop,
|
||||
each = H.each,
|
||||
grep = H.grep,
|
||||
pick = H.pick,
|
||||
Series = H.Series,
|
||||
stableSort = H.stableSort,
|
||||
color = H.Color,
|
||||
eachObject = function(list, func, context) {
|
||||
var key;
|
||||
context = context || this;
|
||||
for (key in list) {
|
||||
if (list.hasOwnProperty(key)) {
|
||||
func.call(context, list[key], key, list);
|
||||
}
|
||||
}
|
||||
},
|
||||
reduce = function(arr, func, previous, context) {
|
||||
context = context || this;
|
||||
arr = arr || []; // @note should each be able to handle empty values automatically?
|
||||
each(arr, function(current, i) {
|
||||
previous = func.call(context, previous, current, i, arr);
|
||||
});
|
||||
return previous;
|
||||
},
|
||||
// @todo find correct name for this function.
|
||||
// @todo Similar to reduce, this function is likely redundant
|
||||
recursive = function(item, func, context) {
|
||||
var next;
|
||||
context = context || this;
|
||||
next = func.call(context, item);
|
||||
if (next !== false) {
|
||||
recursive(next, func, context);
|
||||
}
|
||||
};
|
||||
|
||||
// The Treemap series type
|
||||
seriesType('treemap', 'scatter', {
|
||||
showInLegend: false,
|
||||
marker: false,
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
defer: false,
|
||||
verticalAlign: 'middle',
|
||||
formatter: function() { // #2945
|
||||
return this.point.name || this.point.id;
|
||||
},
|
||||
inside: true
|
||||
},
|
||||
tooltip: {
|
||||
headerFormat: '',
|
||||
pointFormat: '<b>{point.name}</b>: {point.value}</b><br/>'
|
||||
},
|
||||
layoutAlgorithm: 'sliceAndDice',
|
||||
layoutStartingDirection: 'vertical',
|
||||
alternateStartingDirection: false,
|
||||
levelIsConstant: true,
|
||||
drillUpButton: {
|
||||
position: {
|
||||
align: 'right',
|
||||
x: -10,
|
||||
y: 10
|
||||
}
|
||||
},
|
||||
|
||||
// Presentational options
|
||||
borderColor: '#e6e6e6',
|
||||
borderWidth: 1,
|
||||
opacity: 0.15,
|
||||
states: {
|
||||
hover: {
|
||||
borderColor: '#999999',
|
||||
brightness: seriesTypes.heatmap ? 0 : 0.1,
|
||||
opacity: 0.75,
|
||||
shadow: false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Prototype members
|
||||
}, {
|
||||
pointArrayMap: ['value'],
|
||||
axisTypes: seriesTypes.heatmap ? ['xAxis', 'yAxis', 'colorAxis'] : ['xAxis', 'yAxis'],
|
||||
optionalAxis: 'colorAxis',
|
||||
getSymbol: noop,
|
||||
parallelArrays: ['x', 'y', 'value', 'colorValue'],
|
||||
colorKey: 'colorValue', // Point color option key
|
||||
translateColors: seriesTypes.heatmap && seriesTypes.heatmap.prototype.translateColors,
|
||||
trackerGroups: ['group', 'dataLabelsGroup'],
|
||||
/**
|
||||
* Creates an object map from parent id to childrens index.
|
||||
* @param {Array} data List of points set in options.
|
||||
* @param {string} data[].parent Parent id of point.
|
||||
* @param {Array} ids List of all point ids.
|
||||
* @return {Object} Map from parent id to children index in data.
|
||||
*/
|
||||
getListOfParents: function(data, ids) {
|
||||
var listOfParents = reduce(data, function(prev, curr, i) {
|
||||
var parent = pick(curr.parent, '');
|
||||
if (prev[parent] === undefined) {
|
||||
prev[parent] = [];
|
||||
}
|
||||
prev[parent].push(i);
|
||||
return prev;
|
||||
}, {});
|
||||
|
||||
// If parent does not exist, hoist parent to root of tree.
|
||||
eachObject(listOfParents, function(children, parent, list) {
|
||||
if ((parent !== '') && (H.inArray(parent, ids) === -1)) {
|
||||
each(children, function(child) {
|
||||
list[''].push(child);
|
||||
});
|
||||
delete list[parent];
|
||||
}
|
||||
});
|
||||
return listOfParents;
|
||||
},
|
||||
/**
|
||||
* Creates a tree structured object from the series points
|
||||
*/
|
||||
getTree: function() {
|
||||
var tree,
|
||||
series = this,
|
||||
allIds = map(this.data, function(d) {
|
||||
return d.id;
|
||||
}),
|
||||
parentList = series.getListOfParents(this.data, allIds);
|
||||
|
||||
series.nodeMap = [];
|
||||
tree = series.buildNode('', -1, 0, parentList, null);
|
||||
// Parents of the root node is by default visible
|
||||
recursive(this.nodeMap[this.rootNode], function(node) {
|
||||
var next = false,
|
||||
p = node.parent;
|
||||
node.visible = true;
|
||||
if (p || p === '') {
|
||||
next = series.nodeMap[p];
|
||||
}
|
||||
return next;
|
||||
});
|
||||
// Children of the root node is by default visible
|
||||
recursive(this.nodeMap[this.rootNode].children, function(children) {
|
||||
var next = false;
|
||||
each(children, function(child) {
|
||||
child.visible = true;
|
||||
if (child.children.length) {
|
||||
next = (next || []).concat(child.children);
|
||||
}
|
||||
});
|
||||
return next;
|
||||
});
|
||||
this.setTreeValues(tree);
|
||||
return tree;
|
||||
},
|
||||
init: function(chart, options) {
|
||||
var series = this;
|
||||
Series.prototype.init.call(series, chart, options);
|
||||
if (series.options.allowDrillToNode) {
|
||||
series.drillTo();
|
||||
}
|
||||
},
|
||||
buildNode: function(id, i, level, list, parent) {
|
||||
var series = this,
|
||||
children = [],
|
||||
point = series.points[i],
|
||||
node,
|
||||
child;
|
||||
|
||||
// Actions
|
||||
each((list[id] || []), function(i) {
|
||||
child = series.buildNode(series.points[i].id, i, (level + 1), list, id);
|
||||
children.push(child);
|
||||
});
|
||||
node = {
|
||||
id: id,
|
||||
i: i,
|
||||
children: children,
|
||||
level: level,
|
||||
parent: parent,
|
||||
visible: false // @todo move this to better location
|
||||
};
|
||||
series.nodeMap[node.id] = node;
|
||||
if (point) {
|
||||
point.node = node;
|
||||
}
|
||||
return node;
|
||||
},
|
||||
setTreeValues: function(tree) {
|
||||
var series = this,
|
||||
options = series.options,
|
||||
childrenTotal = 0,
|
||||
children = [],
|
||||
val,
|
||||
point = series.points[tree.i];
|
||||
|
||||
// First give the children some values
|
||||
each(tree.children, function(child) {
|
||||
child = series.setTreeValues(child);
|
||||
children.push(child);
|
||||
|
||||
if (!child.ignore) {
|
||||
childrenTotal += child.val;
|
||||
} else {
|
||||
// @todo Add predicate to avoid looping already ignored children
|
||||
recursive(child.children, function(children) {
|
||||
var next = false;
|
||||
each(children, function(node) {
|
||||
extend(node, {
|
||||
ignore: true,
|
||||
isLeaf: false,
|
||||
visible: false
|
||||
});
|
||||
if (node.children.length) {
|
||||
next = (next || []).concat(node.children);
|
||||
}
|
||||
});
|
||||
return next;
|
||||
});
|
||||
}
|
||||
});
|
||||
// Sort the children
|
||||
stableSort(children, function(a, b) {
|
||||
return a.sortIndex - b.sortIndex;
|
||||
});
|
||||
// Set the values
|
||||
val = pick(point && point.options.value, childrenTotal);
|
||||
if (point) {
|
||||
point.value = val;
|
||||
}
|
||||
extend(tree, {
|
||||
children: children,
|
||||
childrenTotal: childrenTotal,
|
||||
// Ignore this node if point is not visible
|
||||
ignore: !(pick(point && point.visible, true) && (val > 0)),
|
||||
isLeaf: tree.visible && !childrenTotal,
|
||||
levelDynamic: (options.levelIsConstant ? tree.level : (tree.level - series.nodeMap[series.rootNode].level)),
|
||||
name: pick(point && point.name, ''),
|
||||
sortIndex: pick(point && point.sortIndex, -val),
|
||||
val: val
|
||||
});
|
||||
return tree;
|
||||
},
|
||||
/**
|
||||
* Recursive function which calculates the area for all children of a node.
|
||||
* @param {Object} node The node which is parent to the children.
|
||||
* @param {Object} area The rectangular area of the parent.
|
||||
*/
|
||||
calculateChildrenAreas: function(parent, area) {
|
||||
var series = this,
|
||||
options = series.options,
|
||||
level = this.levelMap[parent.levelDynamic + 1],
|
||||
algorithm = pick((series[level && level.layoutAlgorithm] && level.layoutAlgorithm), options.layoutAlgorithm),
|
||||
alternate = options.alternateStartingDirection,
|
||||
childrenValues = [],
|
||||
children;
|
||||
|
||||
// Collect all children which should be included
|
||||
children = grep(parent.children, function(n) {
|
||||
return !n.ignore;
|
||||
});
|
||||
|
||||
if (level && level.layoutStartingDirection) {
|
||||
area.direction = level.layoutStartingDirection === 'vertical' ? 0 : 1;
|
||||
}
|
||||
childrenValues = series[algorithm](area, children);
|
||||
each(children, function(child, index) {
|
||||
var values = childrenValues[index];
|
||||
child.values = merge(values, {
|
||||
val: child.childrenTotal,
|
||||
direction: (alternate ? 1 - area.direction : area.direction)
|
||||
});
|
||||
child.pointValues = merge(values, {
|
||||
x: (values.x / series.axisRatio),
|
||||
width: (values.width / series.axisRatio)
|
||||
});
|
||||
// If node has children, then call method recursively
|
||||
if (child.children.length) {
|
||||
series.calculateChildrenAreas(child, child.values);
|
||||
}
|
||||
});
|
||||
},
|
||||
setPointValues: function() {
|
||||
var series = this,
|
||||
xAxis = series.xAxis,
|
||||
yAxis = series.yAxis;
|
||||
each(series.points, function(point) {
|
||||
var node = point.node,
|
||||
values = node.pointValues,
|
||||
x1,
|
||||
x2,
|
||||
y1,
|
||||
y2,
|
||||
crispCorr = 0.5; // Assume 1px borderWidth for simplicity
|
||||
|
||||
// Points which is ignored, have no values.
|
||||
if (values && node.visible) {
|
||||
x1 = Math.round(xAxis.translate(values.x, 0, 0, 0, 1)) - crispCorr;
|
||||
x2 = Math.round(xAxis.translate(values.x + values.width, 0, 0, 0, 1)) - crispCorr;
|
||||
y1 = Math.round(yAxis.translate(values.y, 0, 0, 0, 1)) - crispCorr;
|
||||
y2 = Math.round(yAxis.translate(values.y + values.height, 0, 0, 0, 1)) - crispCorr;
|
||||
// Set point values
|
||||
point.shapeType = 'rect';
|
||||
point.shapeArgs = {
|
||||
x: Math.min(x1, x2),
|
||||
y: Math.min(y1, y2),
|
||||
width: Math.abs(x2 - x1),
|
||||
height: Math.abs(y2 - y1)
|
||||
};
|
||||
point.plotX = point.shapeArgs.x + (point.shapeArgs.width / 2);
|
||||
point.plotY = point.shapeArgs.y + (point.shapeArgs.height / 2);
|
||||
} else {
|
||||
// Reset visibility
|
||||
delete point.plotX;
|
||||
delete point.plotY;
|
||||
}
|
||||
});
|
||||
},
|
||||
setColorRecursive: function(node, color, colorIndex) {
|
||||
var series = this,
|
||||
point,
|
||||
level;
|
||||
if (node) {
|
||||
point = series.points[node.i];
|
||||
level = series.levelMap[node.levelDynamic];
|
||||
// Select either point color, level color or inherited color.
|
||||
color = pick(point && point.options.color, level && level.color, color);
|
||||
colorIndex = pick(point && point.options.colorIndex, level && level.colorIndex, colorIndex);
|
||||
if (point) {
|
||||
point.color = color;
|
||||
point.colorIndex = colorIndex;
|
||||
}
|
||||
|
||||
// Do it all again with the children
|
||||
if (node.children.length) {
|
||||
each(node.children, function(child) {
|
||||
series.setColorRecursive(child, color, colorIndex);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
algorithmGroup: function(h, w, d, p) {
|
||||
this.height = h;
|
||||
this.width = w;
|
||||
this.plot = p;
|
||||
this.direction = d;
|
||||
this.startDirection = d;
|
||||
this.total = 0;
|
||||
this.nW = 0;
|
||||
this.lW = 0;
|
||||
this.nH = 0;
|
||||
this.lH = 0;
|
||||
this.elArr = [];
|
||||
this.lP = {
|
||||
total: 0,
|
||||
lH: 0,
|
||||
nH: 0,
|
||||
lW: 0,
|
||||
nW: 0,
|
||||
nR: 0,
|
||||
lR: 0,
|
||||
aspectRatio: function(w, h) {
|
||||
return Math.max((w / h), (h / w));
|
||||
}
|
||||
};
|
||||
this.addElement = function(el) {
|
||||
this.lP.total = this.elArr[this.elArr.length - 1];
|
||||
this.total = this.total + el;
|
||||
if (this.direction === 0) {
|
||||
// Calculate last point old aspect ratio
|
||||
this.lW = this.nW;
|
||||
this.lP.lH = this.lP.total / this.lW;
|
||||
this.lP.lR = this.lP.aspectRatio(this.lW, this.lP.lH);
|
||||
// Calculate last point new aspect ratio
|
||||
this.nW = this.total / this.height;
|
||||
this.lP.nH = this.lP.total / this.nW;
|
||||
this.lP.nR = this.lP.aspectRatio(this.nW, this.lP.nH);
|
||||
} else {
|
||||
// Calculate last point old aspect ratio
|
||||
this.lH = this.nH;
|
||||
this.lP.lW = this.lP.total / this.lH;
|
||||
this.lP.lR = this.lP.aspectRatio(this.lP.lW, this.lH);
|
||||
// Calculate last point new aspect ratio
|
||||
this.nH = this.total / this.width;
|
||||
this.lP.nW = this.lP.total / this.nH;
|
||||
this.lP.nR = this.lP.aspectRatio(this.lP.nW, this.nH);
|
||||
}
|
||||
this.elArr.push(el);
|
||||
};
|
||||
this.reset = function() {
|
||||
this.nW = 0;
|
||||
this.lW = 0;
|
||||
this.elArr = [];
|
||||
this.total = 0;
|
||||
};
|
||||
},
|
||||
algorithmCalcPoints: function(directionChange, last, group, childrenArea) {
|
||||
var pX,
|
||||
pY,
|
||||
pW,
|
||||
pH,
|
||||
gW = group.lW,
|
||||
gH = group.lH,
|
||||
plot = group.plot,
|
||||
keep,
|
||||
i = 0,
|
||||
end = group.elArr.length - 1;
|
||||
if (last) {
|
||||
gW = group.nW;
|
||||
gH = group.nH;
|
||||
} else {
|
||||
keep = group.elArr[group.elArr.length - 1];
|
||||
}
|
||||
each(group.elArr, function(p) {
|
||||
if (last || (i < end)) {
|
||||
if (group.direction === 0) {
|
||||
pX = plot.x;
|
||||
pY = plot.y;
|
||||
pW = gW;
|
||||
pH = p / pW;
|
||||
} else {
|
||||
pX = plot.x;
|
||||
pY = plot.y;
|
||||
pH = gH;
|
||||
pW = p / pH;
|
||||
}
|
||||
childrenArea.push({
|
||||
x: pX,
|
||||
y: pY,
|
||||
width: pW,
|
||||
height: pH
|
||||
});
|
||||
if (group.direction === 0) {
|
||||
plot.y = plot.y + pH;
|
||||
} else {
|
||||
plot.x = plot.x + pW;
|
||||
}
|
||||
}
|
||||
i = i + 1;
|
||||
});
|
||||
// Reset variables
|
||||
group.reset();
|
||||
if (group.direction === 0) {
|
||||
group.width = group.width - gW;
|
||||
} else {
|
||||
group.height = group.height - gH;
|
||||
}
|
||||
plot.y = plot.parent.y + (plot.parent.height - group.height);
|
||||
plot.x = plot.parent.x + (plot.parent.width - group.width);
|
||||
if (directionChange) {
|
||||
group.direction = 1 - group.direction;
|
||||
}
|
||||
// If not last, then add uncalculated element
|
||||
if (!last) {
|
||||
group.addElement(keep);
|
||||
}
|
||||
},
|
||||
algorithmLowAspectRatio: function(directionChange, parent, children) {
|
||||
var childrenArea = [],
|
||||
series = this,
|
||||
pTot,
|
||||
plot = {
|
||||
x: parent.x,
|
||||
y: parent.y,
|
||||
parent: parent
|
||||
},
|
||||
direction = parent.direction,
|
||||
i = 0,
|
||||
end = children.length - 1,
|
||||
group = new this.algorithmGroup(parent.height, parent.width, direction, plot); // eslint-disable-line new-cap
|
||||
// Loop through and calculate all areas
|
||||
each(children, function(child) {
|
||||
pTot = (parent.width * parent.height) * (child.val / parent.val);
|
||||
group.addElement(pTot);
|
||||
if (group.lP.nR > group.lP.lR) {
|
||||
series.algorithmCalcPoints(directionChange, false, group, childrenArea, plot);
|
||||
}
|
||||
// If last child, then calculate all remaining areas
|
||||
if (i === end) {
|
||||
series.algorithmCalcPoints(directionChange, true, group, childrenArea, plot);
|
||||
}
|
||||
i = i + 1;
|
||||
});
|
||||
return childrenArea;
|
||||
},
|
||||
algorithmFill: function(directionChange, parent, children) {
|
||||
var childrenArea = [],
|
||||
pTot,
|
||||
direction = parent.direction,
|
||||
x = parent.x,
|
||||
y = parent.y,
|
||||
width = parent.width,
|
||||
height = parent.height,
|
||||
pX,
|
||||
pY,
|
||||
pW,
|
||||
pH;
|
||||
each(children, function(child) {
|
||||
pTot = (parent.width * parent.height) * (child.val / parent.val);
|
||||
pX = x;
|
||||
pY = y;
|
||||
if (direction === 0) {
|
||||
pH = height;
|
||||
pW = pTot / pH;
|
||||
width = width - pW;
|
||||
x = x + pW;
|
||||
} else {
|
||||
pW = width;
|
||||
pH = pTot / pW;
|
||||
height = height - pH;
|
||||
y = y + pH;
|
||||
}
|
||||
childrenArea.push({
|
||||
x: pX,
|
||||
y: pY,
|
||||
width: pW,
|
||||
height: pH
|
||||
});
|
||||
if (directionChange) {
|
||||
direction = 1 - direction;
|
||||
}
|
||||
});
|
||||
return childrenArea;
|
||||
},
|
||||
strip: function(parent, children) {
|
||||
return this.algorithmLowAspectRatio(false, parent, children);
|
||||
},
|
||||
squarified: function(parent, children) {
|
||||
return this.algorithmLowAspectRatio(true, parent, children);
|
||||
},
|
||||
sliceAndDice: function(parent, children) {
|
||||
return this.algorithmFill(true, parent, children);
|
||||
},
|
||||
stripes: function(parent, children) {
|
||||
return this.algorithmFill(false, parent, children);
|
||||
},
|
||||
translate: function() {
|
||||
var pointValues,
|
||||
seriesArea,
|
||||
tree,
|
||||
val;
|
||||
|
||||
// Call prototype function
|
||||
Series.prototype.translate.call(this);
|
||||
|
||||
// Assign variables
|
||||
this.rootNode = pick(this.options.rootId, '');
|
||||
// Create a object map from level to options
|
||||
this.levelMap = reduce(this.options.levels, function(arr, item) {
|
||||
arr[item.level] = item;
|
||||
return arr;
|
||||
}, {});
|
||||
tree = this.tree = this.getTree(); // @todo Only if series.isDirtyData is true
|
||||
|
||||
// Calculate plotting values.
|
||||
this.axisRatio = (this.xAxis.len / this.yAxis.len);
|
||||
this.nodeMap[''].pointValues = pointValues = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100
|
||||
};
|
||||
this.nodeMap[''].values = seriesArea = merge(pointValues, {
|
||||
width: (pointValues.width * this.axisRatio),
|
||||
direction: (this.options.layoutStartingDirection === 'vertical' ? 0 : 1),
|
||||
val: tree.val
|
||||
});
|
||||
this.calculateChildrenAreas(tree, seriesArea);
|
||||
|
||||
// Logic for point colors
|
||||
if (this.colorAxis) {
|
||||
this.translateColors();
|
||||
} else if (!this.options.colorByPoint) {
|
||||
this.setColorRecursive(this.tree);
|
||||
}
|
||||
|
||||
// Update axis extremes according to the root node.
|
||||
if (this.options.allowDrillToNode) {
|
||||
val = this.nodeMap[this.rootNode].pointValues;
|
||||
this.xAxis.setExtremes(val.x, val.x + val.width, false);
|
||||
this.yAxis.setExtremes(val.y, val.y + val.height, false);
|
||||
this.xAxis.setScale();
|
||||
this.yAxis.setScale();
|
||||
}
|
||||
|
||||
// Assign values to points.
|
||||
this.setPointValues();
|
||||
},
|
||||
/**
|
||||
* Extend drawDataLabels with logic to handle custom options related to the treemap series:
|
||||
* - Points which is not a leaf node, has dataLabels disabled by default.
|
||||
* - Options set on series.levels is merged in.
|
||||
* - Width of the dataLabel is set to match the width of the point shape.
|
||||
*/
|
||||
drawDataLabels: function() {
|
||||
var series = this,
|
||||
points = grep(series.points, function(n) {
|
||||
return n.node.visible;
|
||||
}),
|
||||
options,
|
||||
level;
|
||||
each(points, function(point) {
|
||||
level = series.levelMap[point.node.levelDynamic];
|
||||
// Set options to new object to avoid problems with scope
|
||||
options = {
|
||||
style: {}
|
||||
};
|
||||
|
||||
// If not a leaf, then label should be disabled as default
|
||||
if (!point.node.isLeaf) {
|
||||
options.enabled = false;
|
||||
}
|
||||
|
||||
// If options for level exists, include them as well
|
||||
if (level && level.dataLabels) {
|
||||
options = merge(options, level.dataLabels);
|
||||
series._hasPointLabels = true;
|
||||
}
|
||||
|
||||
// Set dataLabel width to the width of the point shape.
|
||||
if (point.shapeArgs) {
|
||||
options.style.width = point.shapeArgs.width;
|
||||
if (point.dataLabel) {
|
||||
point.dataLabel.css({
|
||||
width: point.shapeArgs.width + 'px'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Merge custom options with point options
|
||||
point.dlOptions = merge(options, point.options.dataLabels);
|
||||
});
|
||||
Series.prototype.drawDataLabels.call(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Over the alignment method by setting z index
|
||||
*/
|
||||
alignDataLabel: function(point) {
|
||||
seriesTypes.column.prototype.alignDataLabel.apply(this, arguments);
|
||||
if (point.dataLabel) {
|
||||
point.dataLabel.attr({
|
||||
zIndex: point.node.zIndex + 1
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get presentational attributes
|
||||
*/
|
||||
pointAttribs: function(point, state) {
|
||||
var level = this.levelMap[point.node.levelDynamic] || {},
|
||||
options = this.options,
|
||||
attr,
|
||||
stateOptions = (state && options.states[state]) || {},
|
||||
className = point.getClassName(),
|
||||
opacity;
|
||||
|
||||
// Set attributes by precedence. Point trumps level trumps series. Stroke width uses pick
|
||||
// because it can be 0.
|
||||
attr = {
|
||||
'stroke': point.borderColor || level.borderColor || stateOptions.borderColor || options.borderColor,
|
||||
'stroke-width': pick(point.borderWidth, level.borderWidth, stateOptions.borderWidth, options.borderWidth),
|
||||
'dashstyle': point.borderDashStyle || level.borderDashStyle || stateOptions.borderDashStyle || options.borderDashStyle,
|
||||
'fill': point.color || this.color
|
||||
};
|
||||
|
||||
// Hide levels above the current view
|
||||
if (className.indexOf('highcharts-above-level') !== -1) {
|
||||
attr.fill = 'none';
|
||||
attr['stroke-width'] = 0;
|
||||
|
||||
// Nodes with children that accept interaction
|
||||
} else if (className.indexOf('highcharts-internal-node-interactive') !== -1) {
|
||||
opacity = pick(stateOptions.opacity, options.opacity);
|
||||
attr.fill = color(attr.fill).setOpacity(opacity).get();
|
||||
attr.cursor = 'pointer';
|
||||
// Hide nodes that have children
|
||||
} else if (className.indexOf('highcharts-internal-node') !== -1) {
|
||||
attr.fill = 'none';
|
||||
|
||||
} else if (state) {
|
||||
// Brighten and hoist the hover nodes
|
||||
attr.fill = color(attr.fill).brighten(stateOptions.brightness).get();
|
||||
}
|
||||
return attr;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Extending ColumnSeries drawPoints
|
||||
*/
|
||||
drawPoints: function() {
|
||||
var series = this,
|
||||
points = grep(series.points, function(n) {
|
||||
return n.node.visible;
|
||||
});
|
||||
|
||||
each(points, function(point) {
|
||||
var groupKey = 'levelGroup-' + point.node.levelDynamic;
|
||||
if (!series[groupKey]) {
|
||||
series[groupKey] = series.chart.renderer.g(groupKey)
|
||||
.attr({
|
||||
zIndex: 1000 - point.node.levelDynamic // @todo Set the zIndex based upon the number of levels, instead of using 1000
|
||||
})
|
||||
.add(series.group);
|
||||
}
|
||||
point.group = series[groupKey];
|
||||
|
||||
});
|
||||
// Call standard drawPoints
|
||||
seriesTypes.column.prototype.drawPoints.call(this);
|
||||
|
||||
// If drillToNode is allowed, set a point cursor on clickables & add drillId to point
|
||||
if (series.options.allowDrillToNode) {
|
||||
each(points, function(point) {
|
||||
if (point.graphic) {
|
||||
point.drillId = series.options.interactByLeaf ? series.drillToByLeaf(point) : series.drillToByGroup(point);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Add drilling on the suitable points
|
||||
*/
|
||||
drillTo: function() {
|
||||
var series = this;
|
||||
H.addEvent(series, 'click', function(event) {
|
||||
var point = event.point,
|
||||
drillId = point.drillId,
|
||||
drillName;
|
||||
// If a drill id is returned, add click event and cursor.
|
||||
if (drillId) {
|
||||
drillName = series.nodeMap[series.rootNode].name || series.rootNode;
|
||||
point.setState(''); // Remove hover
|
||||
series.drillToNode(drillId);
|
||||
series.showDrillUpButton(drillName);
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Finds the drill id for a parent node.
|
||||
* Returns false if point should not have a click event
|
||||
* @param {Object} point
|
||||
* @return {string || boolean} Drill to id or false when point should not have a click event
|
||||
*/
|
||||
drillToByGroup: function(point) {
|
||||
var series = this,
|
||||
drillId = false;
|
||||
if ((point.node.level - series.nodeMap[series.rootNode].level) === 1 && !point.node.isLeaf) {
|
||||
drillId = point.id;
|
||||
}
|
||||
return drillId;
|
||||
},
|
||||
/**
|
||||
* Finds the drill id for a leaf node.
|
||||
* Returns false if point should not have a click event
|
||||
* @param {Object} point
|
||||
* @return {string || boolean} Drill to id or false when point should not have a click event
|
||||
*/
|
||||
drillToByLeaf: function(point) {
|
||||
var series = this,
|
||||
drillId = false,
|
||||
nodeParent;
|
||||
if ((point.node.parent !== series.rootNode) && (point.node.isLeaf)) {
|
||||
nodeParent = point.node;
|
||||
while (!drillId) {
|
||||
nodeParent = series.nodeMap[nodeParent.parent];
|
||||
if (nodeParent.parent === series.rootNode) {
|
||||
drillId = nodeParent.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return drillId;
|
||||
},
|
||||
drillUp: function() {
|
||||
var drillPoint = null,
|
||||
node,
|
||||
parent;
|
||||
if (this.rootNode) {
|
||||
node = this.nodeMap[this.rootNode];
|
||||
if (node.parent !== null) {
|
||||
drillPoint = this.nodeMap[node.parent];
|
||||
} else {
|
||||
drillPoint = this.nodeMap[''];
|
||||
}
|
||||
}
|
||||
|
||||
if (drillPoint !== null) {
|
||||
this.drillToNode(drillPoint.id);
|
||||
if (drillPoint.id === '') {
|
||||
this.drillUpButton = this.drillUpButton.destroy();
|
||||
} else {
|
||||
parent = this.nodeMap[drillPoint.parent];
|
||||
this.showDrillUpButton((parent.name || parent.id));
|
||||
}
|
||||
}
|
||||
},
|
||||
drillToNode: function(id) {
|
||||
this.options.rootId = id;
|
||||
this.isDirty = true; // Force redraw
|
||||
this.chart.redraw();
|
||||
},
|
||||
showDrillUpButton: function(name) {
|
||||
var series = this,
|
||||
backText = (name || '< Back'),
|
||||
buttonOptions = series.options.drillUpButton,
|
||||
attr,
|
||||
states;
|
||||
|
||||
if (buttonOptions.text) {
|
||||
backText = buttonOptions.text;
|
||||
}
|
||||
if (!this.drillUpButton) {
|
||||
attr = buttonOptions.theme;
|
||||
states = attr && attr.states;
|
||||
|
||||
this.drillUpButton = this.chart.renderer.button(
|
||||
backText,
|
||||
null,
|
||||
null,
|
||||
function() {
|
||||
series.drillUp();
|
||||
},
|
||||
attr,
|
||||
states && states.hover,
|
||||
states && states.select
|
||||
)
|
||||
.attr({
|
||||
align: buttonOptions.position.align,
|
||||
zIndex: 7
|
||||
})
|
||||
.add()
|
||||
.align(buttonOptions.position, false, buttonOptions.relativeTo || 'plotBox');
|
||||
} else {
|
||||
this.drillUpButton.attr({
|
||||
text: backText
|
||||
})
|
||||
.align();
|
||||
}
|
||||
},
|
||||
buildKDTree: noop,
|
||||
drawLegendSymbol: H.LegendSymbolMixin.drawRectangle,
|
||||
getExtremes: function() {
|
||||
// Get the extremes from the value data
|
||||
Series.prototype.getExtremes.call(this, this.colorValueData);
|
||||
this.valueMin = this.dataMin;
|
||||
this.valueMax = this.dataMax;
|
||||
|
||||
// Get the extremes from the y data
|
||||
Series.prototype.getExtremes.call(this);
|
||||
},
|
||||
getExtremesFromAll: true,
|
||||
bindAxes: function() {
|
||||
var treeAxis = {
|
||||
endOnTick: false,
|
||||
gridLineWidth: 0,
|
||||
lineWidth: 0,
|
||||
min: 0,
|
||||
dataMin: 0,
|
||||
minPadding: 0,
|
||||
max: 100,
|
||||
dataMax: 100,
|
||||
maxPadding: 0,
|
||||
startOnTick: false,
|
||||
title: null,
|
||||
tickPositions: []
|
||||
};
|
||||
Series.prototype.bindAxes.call(this);
|
||||
H.extend(this.yAxis.options, treeAxis);
|
||||
H.extend(this.xAxis.options, treeAxis);
|
||||
}
|
||||
|
||||
// Point class
|
||||
}, {
|
||||
getClassName: function() {
|
||||
var className = H.Point.prototype.getClassName.call(this),
|
||||
series = this.series,
|
||||
options = series.options;
|
||||
|
||||
// Above the current level
|
||||
if (this.node.level <= series.nodeMap[series.rootNode].level) {
|
||||
className += ' highcharts-above-level';
|
||||
|
||||
} else if (!this.node.isLeaf && !pick(options.interactByLeaf, !options.allowDrillToNode)) {
|
||||
className += ' highcharts-internal-node-interactive';
|
||||
|
||||
} else if (!this.node.isLeaf) {
|
||||
className += ' highcharts-internal-node';
|
||||
}
|
||||
return className;
|
||||
},
|
||||
setState: function(state) {
|
||||
H.Point.prototype.setState.call(this, state);
|
||||
this.graphic.attr({
|
||||
zIndex: state === 'hover' ? 1 : 0
|
||||
});
|
||||
},
|
||||
setVisible: seriesTypes.pie.prototype.pointClass.prototype.setVisible
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
Reference in New Issue
Block a user