first commit
This commit is contained in:
@ -0,0 +1,46 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
Accessibility module
|
||||
|
||||
(c) 2010-2016 Highsoft AS
|
||||
Author: Oystein Moseng
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(h){"object"===typeof module&&module.exports?module.exports=h:h(Highcharts)})(function(h){(function(e){function h(a){for(var b=a.childNodes.length;b--;)a.appendChild(a.childNodes[b])}function q(a){var b;a&&a.onclick&&(b=l.createEvent("Events"),b.initEvent("click",!0,!1),a.onclick(b))}var y=e.win,l=y.document,g=e.each,A=e.erase,v=e.addEvent,B=e.removeEvent,w=e.fireEvent,C=e.dateFormat,u=e.merge,r={"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"]},D={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. "},E="name id category x value y".split(" "),z="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.length&&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&&h(d),this.points&&(this.points.length<a.pointDescriptionThreshold||!1===a.pointDescriptionThreshold)&&
|
||||
g(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=r[this.type]||r.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,x=b.xAxis&&b.xAxis.isDatetimeAxis,b=x&&C(d.pointDateFormatter&&d.pointDateFormatter(a)||d.pointDateFormat||e.Tooltip.prototype.getXDateFormat(a,b.chart.options.tooltip,b.xAxis),a.x);g(z,function(c){void 0!==a[c]&&(f=!0)});f?(x&&(c=b),g(E.concat(z),function(b){void 0===a[b]||x&&"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;b=this.getExtremes();var c=(b.max-b.min)/d*a,d=b.max+c,c=b.min+c,f=d-c;0>a&&c<b.dataMin?(c=b.dataMin,
|
||||
d=c+f):0<a&&d>b.dataMax&&(d=b.dataMax,c=d-f);this.setExtremes(c,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;g(b.series,function(f){f!==a&&0>b.types.indexOf(a.type)&&(c=!0)});c||A(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."+(D[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&&(g(a,function(b){"DIV"!==
|
||||
b.tagName||b.children&&b.children.length||(b.setAttribute("role","menuitem"),b.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,c=d&&d.index||0;if(!b[0]||!b[0].points)return!1;if(!d)return b[0].points[0].highlight();if(d.series.points[c]!==d)for(var f=0;f<d.series.points.length;++f)if(d.series.points[f]===d){c=f;break}b=b[d.series.index+(a?1:-1)];d=d.series.points[c+(a?1:-1)]||b&&b.points[a?0: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.highlightLegendItem=function(a){var b=
|
||||
this.legend.allItems;b[this.highlightedLegendItemIx]&&w(b[this.highlightedLegendItemIx].legendGroup.element,"mouseout");this.highlightedLegendItemIx=a;return b[a]?(b[a].legendGroup.element.focus&&b[a].legendGroup.element.focus(),w(b[a].legendGroup.element,"mouseover"),!0):!1};e.Chart.prototype.hideExportMenu=function(){var a=this.exportDivElements;if(a){g(a,function(b){w(b,"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,d){return new a(u({keyCodeMap:b,move:function(b){c.keyboardNavigationModuleIndex+=b;var a=c.keyboardNavigationModules[c.keyboardNavigationModuleIndex];if(a){if(a.validate&&!a.validate())return this.move(b);if(a.init)return a.init(b),!0}c.keyboardNavigationModuleIndex=
|
||||
0;c.slipNextTab=!0;return!1}},d))}function d(b){b=b||y.event;var a=c.keyboardNavigationModules[c.keyboardNavigationModuleIndex];9===(b.which||b.keyCode)&&c.slipNextTab?c.slipNextTab=!1:(c.slipNextTab=!1,a&&a.run(b)&&b.preventDefault())}var c=this;a.prototype={run:function(c){var b=this,a=c.which||c.keyCode,f=!1,a=this.transformTabs&&9===a?c.shiftKey?37:39:a;g(this.keyCodeMap,function(d){-1<d[0].indexOf(a)&&(f=!1===d[1].call(b,a,c)?!1:!0)});return f}};c.keyboardNavigationModules=[b([[[37,39],function(b){if(!c.highlightAdjacentPoint(39===
|
||||
b))return this.move(39===b?1:-1)}],[[38,40],function(b){var a;if(c.highlightedPoint)if((a=c.series[c.highlightedPoint.series.index+(38===b?-1:1)])&&a.points[0])a.points[0].highlight();else return this.move(40===b?1:-1)}],[[13,32],function(){c.highlightedPoint&&c.highlightedPoint.firePointEvent("click")}]],{init:function(b){var a=c.series&&c.series[c.series.length-1],a=a&&a.points&&a.points[a.points.length-1];0>b&&a&&a.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(){q(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){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;a=c.mapNavButtons[c.focusedMapNavButtonIx];a.element.focus&&a.element.focus();a.setState(2)}],[[13,32],function(){q(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;g(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&&q(c.rangeSelector.buttons[c.highlightedRangeSelectorItemIx].element)}]],{validate:function(){return c.rangeSelector&&c.rangeSelector.buttons&&c.rangeSelector.buttons.length},init:function(a){g(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){a=9===a&&b.shiftKey||38===a?-1:1;b=c.highlightedInputRangeIx+=a;if(1<b||0>b)return this.move(a);c.rangeSelector[b?"maxInput":"minInput"].focus()}]],{validate:function(){return c.rangeSelector&&c.rangeSelector.inputGroup&&"hidden"!==
|
||||
c.rangeSelector.inputGroup.element.getAttribute("visibility")&&!1!==c.options.rangeSelector.inputEnabled&&c.rangeSelector.minInput&&c.rangeSelector.maxInput},transformTabs:!1,init:function(a){c.highlightedInputRangeIx=0<a?0:1;c.rangeSelector[c.highlightedInputRangeIx?"maxInput":"minInput"].focus()}}),b([[[37,39,38,40],function(a){a=37===a||38===a?-1:1;if(!c.highlightLegendItem(c.highlightedLegendItemIx+a))return this.move(a)}],[[13,32],function(){q(c.legend.allItems[c.highlightedLegendItemIx].legendItem.element.parentNode)}]],
|
||||
{validate:function(){return c.legend&&c.legend.allItems&&!c.colorAxis},init:function(a){g(c.legend.allItems,function(a){a.legendGroup.element.setAttribute("tabindex","-1");a.legendGroup.element.setAttribute("role","button");a.legendGroup.element.setAttribute("aria-label","Toggle visibility of series "+a.name)});c.highlightLegendItem(0<a?0:c.legend.allItems.length-1)}})];c.keyboardNavigationModuleIndex=0;c.renderTo.tabIndex||c.renderTo.setAttribute("tabindex","0");v(c.renderTo,"keydown",d);v(c,"destroy",
|
||||
function(){B(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=l.createElement("div"),h=l.createElement("h3"),n=l.createElement("a"),p=l.createElement("h3"),t={position:"absolute",left:"-9999px",top:"auto",width:"1px",height:"1px",overflow:"hidden"},k=b.types||[],k=(1===k.length&&"pie"===k[0]||"map"===k[0])&&{}||b.getAxesDescription(),m=d[0]&&r[d[0].type]||r.default;g.setAttribute("role","region");
|
||||
g.setAttribute("aria-label","Chart screen reader information.");g.innerHTML=e.screenReaderSectionFormatter&&e.screenReaderSectionFormatter(b)||'\x3cdiv tabindex\x3d"0"\x3eUse regions/landmarks to skip ahead to chart'+(1<d.length?" and navigate between data series":"")+".\x3c/div\x3e\x3ch3\x3eSummary.\x3c/h3\x3e\x3cdiv\x3e"+(c.title.text||"Chart")+(c.subtitle&&c.subtitle.text?". "+c.subtitle.text:"")+"\x3c/div\x3e\x3ch3\x3eLong description.\x3c/h3\x3e\x3cdiv\x3e"+(c.chart.description||"No description available.")+
|
||||
"\x3c/div\x3e\x3ch3\x3eStructure.\x3c/h3\x3e\x3cdiv\x3eChart type: "+(c.chart.typeDescription||b.getTypeDescription())+"\x3c/div\x3e"+(1===d.length?"\x3cdiv\x3e"+m[0]+" with "+d[0].points.length+" "+(1===d[0].points.length?m[1]:m[2])+".\x3c/div\x3e":"")+(k.xAxis?"\x3cdiv\x3e"+k.xAxis+"\x3c/div\x3e":"")+(k.yAxis?"\x3cdiv\x3e"+k.yAxis+"\x3c/div\x3e":"");b.getCSV&&(n.innerHTML="View as data table.",n.href="#"+a,n.setAttribute("tabindex","-1"),n.onclick=e.onTableAnchorClick||function(){b.viewData();l.getElementById(a).focus()},
|
||||
h.appendChild(n),g.appendChild(h));p.innerHTML="Chart graphic.";b.renderTo.insertBefore(p,b.renderTo.firstChild);b.renderTo.insertBefore(g,b.renderTo.firstChild);u(!0,p.style,t);u(!0,g.style,t)};e.Chart.prototype.callbacks.push(function(a){var b=a.options,d=b.accessibility;if(d.enabled){var c=l.createElementNS("http://www.w3.org/2000/svg","title"),f=l.createElementNS("http://www.w3.org/2000/svg","g"),h=a.container.getElementsByTagName("desc")[0],q=a.container.getElementsByTagName("text"),n="highcharts-title-"+
|
||||
a.index,p="highcharts-data-table-"+a.index,t=b.title.text||"Chart",k=b.exporting&&b.exporting.csv&&b.exporting.csv.columnHeaderFormatter,m=[];c.textContent=t;c.id=n;h.parentNode.insertBefore(c,h);a.renderTo.setAttribute("role","region");a.renderTo.setAttribute("aria-label",t+". Use up and down arrows to navigate.");if(a.exportSVGElements&&a.exportSVGElements[0]&&a.exportSVGElements[0].element){var r=a.exportSVGElements[0].element.onclick,c=a.exportSVGElements[0].element.parentNode;a.exportSVGElements[0].element.onclick=
|
||||
function(){r.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)}a.rangeSelector&&g(["minInput","maxInput"],function(b,c){a.rangeSelector[b]&&(a.rangeSelector[b].setAttribute("tabindex",
|
||||
"-1"),a.rangeSelector[b].setAttribute("role","textbox"),a.rangeSelector[b].setAttribute("aria-label","Select "+(c?"end":"start")+" date."))});g(q,function(a){a.setAttribute("aria-hidden","true")});a.addScreenReaderRegion(p);d.keyboardNavigation&&a.addKeyboardNavEvents();u(!0,b.exporting,{csv:{columnHeaderFormatter:function(a,b,c){var d=m[m.length-1];1<c&&(d&&d.text)!==a.name&&m.push({text:a.name,span:c});return k?k.call(this,a,b,c):1<c?b:a.name}}});e.wrap(a,"getTable",function(a){return a.apply(this,
|
||||
Array.prototype.slice.call(arguments,1)).replace("\x3ctable\x3e",'\x3ctable id\x3d"'+p+'" summary\x3d"Table representation of chart"\x3e\x3ccaption\x3e'+t+"\x3c/caption\x3e")});e.wrap(a,"viewData",function(a){if(!this.insertedTable){a.apply(this,Array.prototype.slice.call(arguments,1));var b=l.getElementById(p),c=b.getElementsByTagName("tbody")[0],d=c.firstChild.children,e="\x3ctr\x3e\x3ctd\x3e\x3c/td\x3e",f,h;b.setAttribute("tabindex","-1");g(c.children,function(a){f=a.firstChild;h=l.createElement("th");
|
||||
h.setAttribute("scope","row");h.innerHTML=f.innerHTML;f.parentNode.replaceChild(h,f)});g(d,function(a){"TH"===a.tagName&&a.setAttribute("scope","col")});m.length&&(g(m,function(a){e+='\x3cth scope\x3d"col" colspan\x3d"'+a.span+'"\x3e'+a.text+"\x3c/th\x3e"}),c.insertAdjacentHTML("afterbegin",e))}})}})})(h)});
|
||||
1072
static/plugin/chart/highcharts/code/js/modules/accessibility.src.js
Normal file
1072
static/plugin/chart/highcharts/code/js/modules/accessibility.src.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,14 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
|
||||
(c) 2009-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(n){"object"===typeof module&&module.exports?module.exports=n:n(Highcharts)})(function(n){(function(f){var q=f.defined,k=f.isNumber,n=f.inArray,v=f.isArray,w=f.merge,B=f.Chart,x=f.extend,C=f.each,r,D;D=["path","rect","circle"];r={top:0,left:0,center:.5,middle:.5,bottom:1,right:1};var E=function(){this.init.apply(this,arguments)};E.prototype={init:function(a,d){var c=d.shape&&d.shape.type;this.chart=a;var b;b={xAxis:0,yAxis:0,title:{style:{},text:"",x:0,y:0},shape:{params:{stroke:"#000000",
|
||||
fill:"transparent",strokeWidth:2}}};a={circle:{params:{x:0,y:0}}};a[c]&&(b.shape=w(b.shape,a[c]));this.options=w({},b,d)},render:function(a){var d=this.chart,c=this.chart.renderer,b=this.group,f=this.title,e=this.shape,h=this.options,k=h.title,p=h.shape;b||(b=this.group=c.g());!e&&p&&-1!==n(p.type,D)&&(e=this.shape=c[h.shape.type](p.params),e.add(b));!f&&k&&(f=this.title=c.label(k),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,h=d.xAxis[a.xAxis],d=d.yAxis[a.yAxis],y=a.width,p=a.height,z=r[a.anchorY],A=r[a.anchorX],t,l,g,u;e&&(t=e instanceof f.Point?"point":e instanceof f.Series?"series":null,"point"===t?(a.xValue=e.x,a.yValue=e.y,l=e.series):"series"===t&&(l=e),c.visibility!==l.group.visibility&&c.attr({visibility:l.group.visibility}));e=q(a.xValue)?h.toPixels(a.xValue+h.minPointOffset)-h.minPixelPadding:a.x;l=q(a.yValue)?d.toPixels(a.yValue):a.y;if(k(e)&&
|
||||
k(l)){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<n(g,["width","x"])?b[g]=h.translate(b[g]):-1<n(g,["height","y"])&&(b[g]=d.translate(b[g]));b.width&&(b.width-=h.toPixels(0)-h.left);b.x&&(b.x+=h.minPixelPadding);if("path"===a.shape.type){g=b.d;t=e;for(var v=l,w=g.length,m=0;m<w;)k(g[m])&&k(g[m+1])?(g[m]=h.toPixels(g[m])-t,g[m+1]=d.toPixels(g[m+1])-v,m+=2):m+=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(p)||(u||(u=c.getBBox()),p=u.height);k(A)||(A=r.center);k(z)||(z=r.center);e-=y*A;l-=p*z;q(c.translateX)&&q(c.translateY)?c.animate({translateX:e,translateY:l}):c.translate(e,l)}},destroy:function(){var a=this,d=this.chart.annotations.allItems,c=d.indexOf(a);-1<c&&d.splice(c,1);C(["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(B.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 E(b,a[e]),c.push(f),f.render(d)},redraw:function(){C(this.allItems,function(a){a.redraw()})}}});B.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);f.addEvent(a,"redraw",function(){a.annotations.redraw()})})})(n)});
|
||||
@ -0,0 +1,408 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
*
|
||||
* (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
static/plugin/chart/highcharts/code/js/modules/boost.js
Normal file
22
static/plugin/chart/highcharts/code/js/modules/boost.js
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
Boost module
|
||||
|
||||
(c) 2010-2016 Highsoft AS
|
||||
Author: Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(p){"object"===typeof module&&module.exports?module.exports=p:p(Highcharts)})(function(p){(function(f){function p(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(){p(a,b,c,e,h)}):c&&c())}var x=f.win.document,U=function(){},V=f.Color,n=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 c=this.options.stacking&&("translate"===a||"generatePoints"===a);if((this.processedXData||this.options.data).length<(this.options.boostThreshold||Number.MAX_VALUE)||c)"render"===a&&this.image&&(this.image.attr({href:""}),this.animate=null),b.call(this);else if(this[a+"Canvas"])this[a+"Canvas"]()}l(n.prototype,a,b);"translate"===a&&(d.column&&l(d.column.prototype,
|
||||
a,b),d.arearange&&l(d.arearange.prototype,a,b))});l(n.prototype,"getExtremes",function(a){this.hasExtremes()||a.apply(this,Array.prototype.slice.call(arguments,1))});l(n.prototype,"setData",function(a){this.hasExtremes(!0)||a.apply(this,Array.prototype.slice.call(arguments,1))});l(n.prototype,"processData",function(a){this.hasExtremes(!0)||a.apply(this,Array.prototype.slice.call(arguments,1))});f.extend(n.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,n=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);p(P?a.data:l||q,function(b,f){var d,g,k,l="undefined"===typeof c.index,m=!0;if(!l){R?(d=b[0],g=b[1]):(d=b,g=n[f]);O?(R&&(g=b.slice(1,3)),k=g[0],g=g[1]):P&&(d=b.x,g=b.stackY,k=g-b.y);b=null===g;fa||(m=g>=z&&g<=aa);if(!b&&d>=v&&d<=x&&m)if(d=Math.round(e.toPixels(d,!0)),ba){if(void 0===t||d===A){O||(k=g);if(void 0===w||g>D)D=g,w=f;if(void 0===t||k<C)C=k,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=b&&!ga;0===f%5E4&&a.canvasToSVG()}return!l},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,d){a.rect(b-1,c,1,d-c)},fill:!0,sampling:!0});
|
||||
n.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(n.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(n.prototype,"searchPoint",function(a){return this.getPoint(a.apply(this,
|
||||
[].slice.call(arguments,1)))})})(p)});
|
||||
636
static/plugin/chart/highcharts/code/js/modules/boost.src.js
Normal file
636
static/plugin/chart/highcharts/code/js/modules/boost.src.js
Normal file
@ -0,0 +1,636 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
* 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. Modify the heatmap-canvas demo so that it uses this module.
|
||||
* - 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));
|
||||
}));
|
||||
@ -0,0 +1,14 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
|
||||
(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(f){function h(){return Array.prototype.slice.call(arguments,1)}function u(g){g.apply(this);this.drawBreaks(this.xAxis,["x"]);this.drawBreaks(this.yAxis,r(this.pointArrayMap,["y"]))}var r=f.pick,p=f.wrap,t=f.each,x=f.extend,v=f.fireEvent,q=f.Axis,y=f.Series;x(q.prototype,{isInBreak:function(g,c){var d=g.repeat||Infinity,b=g.from,a=g.to-g.from;c=c>=b?(c-b)%d:d-(b-c)%d;return g.inclusive?c<=a:
|
||||
c<a&&0!==c},isInAnyBreak:function(g,c){var d=this.options.breaks,b=d&&d.length,a,e,m;if(b){for(;b--;)this.isInBreak(d[b],g)&&(a=!0,e||(e=r(d[b].showPoints,this.isXAxis?!1:!0)));m=a&&c?a&&!e:a}return m}});p(q.prototype,"setTickPositions",function(g){g.apply(this,Array.prototype.slice.call(arguments,1));if(this.options.breaks){var c=this.tickPositions,d=this.tickPositions.info,b=[],a;for(a=0;a<c.length;a++)this.isInAnyBreak(c[a])||b.push(c[a]);this.tickPositions=b;this.tickPositions.info=d}});p(q.prototype,
|
||||
"init",function(g,c,d){d.breaks&&d.breaks.length&&(d.ordinal=!1);g.call(this,c,d);if(this.options.breaks){var b=this;b.isBroken=!0;this.val2lin=function(a){var e=a,m,d;for(d=0;d<b.breakArray.length;d++)if(m=b.breakArray[d],m.to<=a)e-=m.len;else if(m.from>=a)break;else if(b.isInBreak(m,a)){e-=a-m.from;break}return e};this.lin2val=function(a){var e,d;for(d=0;d<b.breakArray.length&&!(e=b.breakArray[d],e.from>=a);d++)e.to<a?a+=e.len:b.isInBreak(e,a)&&(a+=e.len);return a};this.setExtremes=function(a,b,
|
||||
d,g,c){for(;this.isInAnyBreak(a);)a-=this.closestPointRange;for(;this.isInAnyBreak(b);)b-=this.closestPointRange;q.prototype.setExtremes.call(this,a,b,d,g,c)};this.setAxisTranslation=function(a){q.prototype.setAxisTranslation.call(this,a);var e=b.options.breaks;a=[];var d=[],g=0,c,k,n=b.userMin||b.min,f=b.userMax||b.max,l,h;for(h in e)k=e[h],c=k.repeat||Infinity,b.isInBreak(k,n)&&(n+=k.to%c-n%c),b.isInBreak(k,f)&&(f-=f%c-k.from%c);for(h in e){k=e[h];l=k.from;for(c=k.repeat||Infinity;l-c>n;)l-=c;for(;l<
|
||||
n;)l+=c;for(;l<f;l+=c)a.push({value:l,move:"in"}),a.push({value:l+(k.to-k.from),move:"out",size:k.breakSize})}a.sort(function(a,b){return a.value===b.value?("in"===a.move?0:1)-("in"===b.move?0:1):a.value-b.value});e=0;l=n;for(h in a)k=a[h],e+="in"===k.move?1:-1,1===e&&"in"===k.move&&(l=k.value),0===e&&(d.push({from:l,to:k.value,len:k.value-l-(k.size||0)}),g+=k.value-l-(k.size||0));b.breakArray=d;v(b,"afterBreaks");b.transA*=(f-b.min)/(f-n-g);b.min=n;b.max=f}}});p(y.prototype,"generatePoints",function(g){g.apply(this,
|
||||
h(arguments));var c=this.xAxis,d=this.yAxis,b=this.points,a,e=b.length,f=this.options.connectNulls,w;if(c&&d&&(c.options.breaks||d.options.breaks))for(;e--;)a=b[e],w=null===a.y&&!1===f,w||!c.isInAnyBreak(a.x,!0)&&!d.isInAnyBreak(a.y,!0)||(b.splice(e,1),this.data[e]&&this.data[e].destroyElements())});f.Series.prototype.drawBreaks=function(g,c){var d=this,b=d.points,a,e,f,h;t(c,function(c){a=g.breakArray||[];e=g.isXAxis?g.min:r(d.options.threshold,g.min);t(b,function(b){h=r(b["stack"+c.toUpperCase()],
|
||||
b[c]);t(a,function(a){f=!1;if(e<a.from&&h>a.to||e>a.from&&h<a.from)f="pointBreak";else if(e<a.from&&h>a.from&&h<a.to||e>a.from&&h>a.to&&h<a.from)f="pointInBreak";f&&v(g,f,{point:b,brk:a})})})})};p(f.seriesTypes.column.prototype,"drawPoints",u);p(f.Series.prototype,"drawPoints",u)})(h)});
|
||||
@ -0,0 +1,334 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
*
|
||||
* (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
static/plugin/chart/highcharts/code/js/modules/data.js
Normal file
26
static/plugin/chart/highcharts/code/js/modules/data.js
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
Data module
|
||||
|
||||
(c) 2012-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(p){"object"===typeof module&&module.exports?module.exports=p:p(Highcharts)})(function(p){(function(g){var p=g.win.document,m=g.each,z=g.pick,w=g.inArray,x=g.isNumber,A=g.splat,n,u=function(b,a){this.init(b,a)};g.extend(u.prototype,{init:function(b,a){this.options=b;this.chartOptions=a;this.columns=b.columns||this.rowsToColumns(b.rows)||[];this.firstRowAsNames=z(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,d=[],f=function(b){return(g.seriesTypes[b||"line"].prototype.pointArrayMap||[0]).length},e=b&&b.chart&&b.chart.type,c=[],k=[],t=0,h;m(b&&b.series||[],function(b){c.push(f(b.type||e))});m(a&&a.seriesMapping||[],function(b){d.push(b.x||0)});0===d.length&&d.push(0);m(a&&a.seriesMapping||[],function(a){var d=new n,r,v=c[t]||f(e),
|
||||
q=g.seriesTypes[((b&&b.series||[])[t]||{}).type||e||"line"].prototype.pointArrayMap||["y"];d.addColumnReader(a.x,"x");for(r in a)a.hasOwnProperty(r)&&"x"!==r&&d.addColumnReader(a[r],r);for(h=0;h<v;h++)d.hasReader(q[h])||d.addColumnReader(void 0,q[h]);k.push(d);t++});a=g.seriesTypes[e||"line"].prototype.pointArrayMap;void 0===a&&(a=["y"]);this.valueCount={global:f(e),xColumns:d,individual:c,seriesBuilders:k,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,d=a.csv,f=this.columns,e=a.startRow||0,c=a.endRow||Number.MAX_VALUE,k=a.startColumn||0,t=a.endColumn||Number.MAX_VALUE,h,g,y=0;d&&(g=d.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split(a.lineDelimiter||"\n"),h=a.itemDelimiter||(-1!==d.indexOf("\t")?"\t":","),m(g,function(a,d){var g=b.trim(a),r=0===g.indexOf("#");d>=e&&d<=c&&!r&&""!==g&&(a=
|
||||
a.split(h),m(a,function(b,a){a>=k&&a<=t&&(f[a-k]||(f[a-k]=[]),f[a-k][y]=b)}),y+=1)}),this.dataFound())},parseTable:function(){var b=this.options,a=b.table,d=this.columns,f=b.startRow||0,e=b.endRow||Number.MAX_VALUE,c=b.startColumn||0,k=b.endColumn||Number.MAX_VALUE;a&&("string"===typeof a&&(a=p.getElementById(a)),m(a.getElementsByTagName("tr"),function(b,a){a>=f&&a<=e&&m(b.children,function(b,e){("TD"===b.tagName||"TH"===b.tagName)&&e>=c&&e<=k&&(d[e-c]||(d[e-c]=[]),d[e-c][a-f]=b.innerHTML)})}),this.dataFound())},
|
||||
parseGoogleSpreadsheet:function(){var b=this,a=this.options,d=a.googleSpreadsheetKey,f=this.columns,e=a.startRow||0,c=a.endRow||Number.MAX_VALUE,k=a.startColumn||0,g=a.endColumn||Number.MAX_VALUE,h,v;d&&jQuery.ajax({dataType:"json",url:"https://spreadsheets.google.com/feeds/cells/"+d+"/"+(a.googleSpreadsheetWorksheet||"od6")+"/public/values?alt\x3djson-in-script\x26callback\x3d?",error:a.error,success:function(a){a=a.feed.entry;var d,t=a.length,q=0,n=0,l;for(l=0;l<t;l++)d=a[l],q=Math.max(q,d.gs$cell.col),
|
||||
n=Math.max(n,d.gs$cell.row);for(l=0;l<q;l++)l>=k&&l<=g&&(f[l-k]=[],f[l-k].length=Math.min(n,c-e));for(l=0;l<t;l++)d=a[l],h=d.gs$cell.row-1,v=d.gs$cell.col-1,v>=k&&v<=g&&h>=e&&h<=c&&(f[v-k][h-e]=d.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 d=this.rawColumns,f=this.columns,e=b.length,c,k,g,h,n=this.firstRowAsNames,m=-1!==w(a,this.valueCount.xColumns),r=[],p=this.chartOptions,q,u=(this.options.columnTypes||[])[a],p=m&&(p&&p.xAxis&&"category"===A(p.xAxis)[0].type||"string"===u);for(d[a]||(d[a]=[]);e--;)c=r[e]||b[e],g=this.trim(c),h=this.trim(c,!0),k=parseFloat(h),void 0===d[a][e]&&(d[a][e]=g),p||0===e&&n?b[e]=g:+h===k?(b[e]=k,31536E6<k&&"float"!==u?b.isDatetime=
|
||||
!0:b.isNumeric=!0,void 0!==b[e+1]&&(q=k>b[e+1])):(k=this.parseDate(c),m&&x(k)&&"float"!==u?(r[e]=c,b[e]=k,b.isDatetime=!0,void 0!==b[e+1]&&(c=k>b[e+1],c!==q&&void 0!==q&&(this.alternativeFormat?(this.dateFormat=this.alternativeFormat,e=b.length,this.alternativeFormat=this.dateFormats[this.dateFormat].alternative):b.unsorted=!0),q=c)):(b[e]=""===g?null:g,0!==e&&(b.isDatetime||b.isNumeric)&&(b.mixed=!0)));m&&b.mixed&&(f[a]=d[a]);if(m&&q&&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,d,f,e=this.options.dateFormat||this.dateFormat,c;if(a)d=a(b);else if("string"===typeof b){if(e)a=this.dateFormats[e],(c=b.match(a.regex))&&(d=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;d=a.parser(c);break}c||(c=Date.parse(b),"object"===typeof c&&null!==c&&c.getTime?d=c.getTime()-6E4*c.getTimezoneOffset():x(c)&&(d=c-6E4*(new Date(c)).getTimezoneOffset()))}return d},rowsToColumns:function(b){var a,d,f,e,c;if(b)for(c=[],d=b.length,a=0;a<d;a++)for(e=b[a].length,f=0;f<e;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 d,f=[],
|
||||
e=[],c;for(d=0;d<b;d+=1)f.push(!0);for(b=0;b<a.length;b+=1)for(c=a[b].getReferencedColumnIndexes(),d=0;d<c.length;d+=1)f[c[d]]=!1;for(d=0;d<f.length;d+=1)f[d]&&e.push(d);return e},complete:function(){var b=this.columns,a,d=this.options,f,e,c,k,g=[],h;if(d.complete||d.afterComplete){for(c=0;c<b.length;c++)this.firstRowAsNames&&(b[c].name=b[c].shift());f=[];e=this.getFreeIndexes(b.length,this.valueCount.seriesBuilders);for(c=0;c<this.valueCount.seriesBuilders.length;c++)h=this.valueCount.seriesBuilders[c],
|
||||
h.populateColumns(e)&&g.push(h);for(;0<e.length;){h=new n;h.addColumnReader(0,"x");c=w(0,e);-1!==c&&e.splice(c,1);for(c=0;c<this.valueCount.global;c++)h.addColumnReader(void 0,this.valueCount.globalPointArrayMap[c]);h.populateColumns(e)&&g.push(h)}0<g.length&&0<g[0].readers.length&&(h=b[g[0].readers[0].columnIndex],void 0!==h&&(h.isDatetime?a="datetime":h.isNumeric||(a="category")));if("category"===a)for(c=0;c<g.length;c++)for(h=g[c],e=0;e<h.readers.length;e++)"x"===h.readers[e].configName&&(h.readers[e].configName=
|
||||
"name");for(c=0;c<g.length;c++){h=g[c];e=[];for(k=0;k<b[0].length;k++)e[k]=h.read(b,k);f[c]={data:e};h.name&&(f[c].name=h.name);"category"===a&&(f[c].turboThreshold=0)}b={series:f};a&&(b.xAxis={type:a},"category"===a&&(b.xAxis.uniqueNames=!1));d.complete&&d.complete(b);d.afterComplete&&d.afterComplete(b)}}});g.Data=u;g.data=function(b,a){return new u(b,a)};g.wrap(g.Chart.prototype,"init",function(b,a,d){var f=this;a&&a.data?g.data(g.extend(a.data,{afterComplete:function(e){var c,k;if(a.hasOwnProperty("series"))if("object"===
|
||||
typeof a.series)for(c=Math.max(a.series.length,e.series.length);c--;)k=a.series[c]||{},a.series[c]=g.merge(k,e.series[c]);else delete a.series;a=g.merge(e,a);b.call(f,a,d)}}),a):b.call(f,a,d)});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 d=this.pointIsArray,f=d?
|
||||
[]:{},e;m(this.readers,function(c){var e=b[c.columnIndex][a];d?f.push(e):f[c.configName]=e});void 0===this.name&&2<=this.readers.length&&(e=this.getReferencedColumnIndexes(),2<=e.length&&(e.shift(),e.sort(),this.name=b[e.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=[],d;for(b=0;b<this.readers.length;b+=1)d=this.readers[b],
|
||||
void 0!==d.columnIndex&&a.push(d.columnIndex);return a};n.prototype.hasReader=function(b){var a,d;for(a=0;a<this.readers.length;a+=1)if(d=this.readers[a],d.configName===b)return!0}})(p)});
|
||||
981
static/plugin/chart/highcharts/code/js/modules/data.src.js
Normal file
981
static/plugin/chart/highcharts/code/js/modules/data.src.js
Normal file
@ -0,0 +1,981 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
* 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.uniqueNames = 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));
|
||||
}));
|
||||
25
static/plugin/chart/highcharts/code/js/modules/drilldown.js
Normal file
25
static/plugin/chart/highcharts/code/js/modules/drilldown.js
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
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(f){function n(a,b,d){var c;b.rgba.length&&a.rgba.length?(a=a.rgba,b=b.rgba,c=1!==b[3]||1!==a[3],a=(c?"rgba(":"rgb(")+Math.round(b[0]+(a[0]-b[0])*(1-d))+","+Math.round(b[1]+(a[1]-b[1])*(1-d))+","+Math.round(b[2]+(a[2]-b[2])*(1-d))+(c?","+(b[3]+(a[3]-b[3])*(1-d)):"")+")"):a=b.input||"none";return a}var C=f.noop,v=f.color,w=f.defaultOptions,l=f.each,p=f.extend,H=f.format,y=f.pick,x=f.wrap,q=f.Chart,
|
||||
t=f.seriesTypes,D=t.pie,r=t.column,E=f.Tick,z=f.fireEvent,F=f.inArray,G=1;l(["fill","stroke"],function(a){f.Fx.prototype[a+"Setter"]=function(){this.elem.attr(a,n(v(this.start),v(this.end),this.pos))}});p(w.lang,{drillUpText:"\u25c1 Back to {series.name}"});w.drilldown={animation:{duration:500},drillUpButton:{position:{align:"right",x:-10,y:10}}};f.SVGRenderer.prototype.Element.prototype.fadeIn=function(a){this.attr({opacity:.1,visibility:"inherit"}).animate({opacity:y(this.newOpacity,1)},a||{duration:250})};
|
||||
q.prototype.addSeriesAsDrilldown=function(a,b){this.addSingleSeriesAsDrilldown(a,b);this.applyDrilldown()};q.prototype.addSingleSeriesAsDrilldown=function(a,b){var d=a.series,c=d.xAxis,e=d.yAxis,h,g=[],k=[],u,m,A;A={colorIndex:y(a.colorIndex,d.colorIndex)};this.drilldownLevels||(this.drilldownLevels=[]);u=d.options._levelNumber||0;(m=this.drilldownLevels[this.drilldownLevels.length-1])&&m.levelNumber!==u&&(m=void 0);b=p(p({_ddSeriesId:G++},A),b);h=F(a,d.points);l(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||u,m?(g=m.levelSeries,k=m.levelSeriesOptions):(g.push(a),k.push(a.options)))});a=p({levelNumber:u,seriesOptions:d.options,levelSeriesOptions:k,levelSeries:g,shapeArgs:a.shapeArgs,bBox:a.graphic?a.graphic.getBBox():{},color:a.isNull?(new f.Color(v)).setOpacity(0).get():v,lowerSeriesOptions:b,pointOptions:d.options.data[h],pointIndex:h,oldExtremes:{xMin:c&&
|
||||
c.userMin,xMax:c&&c.userMax,yMin:e&&e.userMin,yMax:e&&e.userMax}},A);this.drilldownLevels.push(a);b=a.lowerSeries=this.addSeries(b,!1);b.options._levelNumber=u+1;c&&(c.oldPos=c.pos,c.userMin=c.userMax=null,e.userMin=e.userMax=null);d.type===b.type&&(b.animate=b.animateDrilldown||C,b.options.animation=!0)};q.prototype.applyDrilldown=function(){var a=this.drilldownLevels,b;a&&0<a.length&&(b=a[a.length-1].levelNumber,l(this.drilldownLevels,function(a){a.levelNumber===b&&l(a.levelSeries,function(a){a.options&&
|
||||
a.options._levelNumber===b&&a.remove(!1)})}));this.redraw();this.showDrillUpButton()};q.prototype.getDrilldownBackText=function(){var a=this.drilldownLevels;if(a&&0<a.length)return a=a[a.length-1],a.series=a.seriesOptions,H(this.options.lang.drillUpText,a)};q.prototype.showDrillUpButton=function(){var a=this,b=this.getDrilldownBackText(),d=a.options.drilldown.drillUpButton,c,e;this.drillUpButton?this.drillUpButton.attr({text:b}).align():(e=(c=d.theme)&&c.states,this.drillUpButton=this.renderer.button(b,
|
||||
null,null,function(){a.drillUp()},c,e&&e.hover,e&&e.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 a=this,b=a.drilldownLevels,d=b[b.length-1].levelNumber,c=b.length,e=a.series,h,g,k,f,m=function(c){var b;l(e,function(a){a.options._ddSeriesId===c._ddSeriesId&&(b=a)});b=b||a.addSeries(c,!1);b.type===k.type&&b.animateDrillupTo&&(b.animate=b.animateDrillupTo);c===g.seriesOptions&&
|
||||
(f=b)};c--;)if(g=b[c],g.levelNumber===d){b.pop();k=g.lowerSeries;if(!k.chart)for(h=e.length;h--;)if(e[h].options.id===g.lowerSeriesOptions.id&&e[h].options._levelNumber===d+1){k=e[h];break}k.xData=[];l(g.levelSeriesOptions,m);z(a,"drillup",{seriesOptions:g.seriesOptions});f.type===k.type&&(f.drilldownLevel=g,f.options.animation=a.options.drilldown.animation,k.animateDrillupFrom&&k.chart&&k.animateDrillupFrom(g));f.options._levelNumber=d;k.remove(!1);f.xAxis&&(h=g.oldExtremes,f.xAxis.setExtremes(h.xMin,
|
||||
h.xMax,!1),f.yAxis.setExtremes(h.yMin,h.yMax,!1))}z(a,"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(a){if(!a){var b=this,d=b.drilldownLevel;l(this.points,function(a){a.graphic&&a.graphic.hide();a.dataLabel&&a.dataLabel.hide();a.connector&&a.connector.hide()});setTimeout(function(){b.points&&
|
||||
l(b.points,function(a,b){b=b===(d&&d.pointIndex)?"show":"fadeIn";var c="show"===b?!0:void 0;if(a.graphic)a.graphic[b](c);if(a.dataLabel)a.dataLabel[b](c);if(a.connector)a.connector[b](c)})},Math.max(this.chart.options.drilldown.animation.duration-50,0));this.animate=C}};r.prototype.animateDrilldown=function(a){var b=this,d=this.chart.drilldownLevels,c,e=this.chart.options.drilldown.animation,h=this.xAxis;a||(l(d,function(a){b.options._ddSeriesId===a.lowerSeriesOptions._ddSeriesId&&(c=a.shapeArgs)}),
|
||||
c.x+=y(h.oldPos,h.pos)-h.pos,l(this.points,function(a){a.graphic&&a.graphic.attr(c).animate(p(a.shapeArgs,{fill:a.color||b.color}),e);a.dataLabel&&a.dataLabel.fadeIn(e)}),this.animate=null)};r.prototype.animateDrillupFrom=function(a){var b=this.chart.options.drilldown.animation,d=this.group,c=this;l(c.trackerGroups,function(a){if(c[a])c[a].on("mouseover")});delete this.group;l(this.points,function(c){var e=c.graphic,g=a.shapeArgs,k=function(){e.destroy();d&&(d=d.destroy())};e&&(delete c.graphic,b?
|
||||
e.animate(g,f.merge(b,{complete:k})):(e.attr(g),k()))})};D&&p(D.prototype,{supportsDrilldown:!0,animateDrillupTo:r.prototype.animateDrillupTo,animateDrillupFrom:r.prototype.animateDrillupFrom,animateDrilldown:function(a){var b=this.chart.options.drilldown.animation,d=this.chart.drilldownLevels[this.chart.drilldownLevels.length-1].shapeArgs,c=d.start,e=(d.end-c)/this.points.length;a||(l(this.points,function(a,g){var h=a.shapeArgs;if(a.graphic)a.graphic.attr(f.merge(d,{start:c+g*e,end:c+(g+1)*e}))[b?
|
||||
"animate":"attr"](h,b)}),this.animate=null)}});f.Point.prototype.doDrilldown=function(a,b,d){var c=this.series.chart,e=c.options.drilldown,f=(e.series||[]).length,g;c.ddDupes||(c.ddDupes=[]);for(;f--&&!g;)e.series[f].id===this.drilldown&&-1===F(this.drilldown,c.ddDupes)&&(g=e.series[f],c.ddDupes.push(this.drilldown));z(c,"drilldown",{point:this,seriesOptions:g,category:b,originalEvent:d,points:void 0!==b&&this.series.xAxis.getDDPoints(b).slice(0)},function(b){var c=b.point.series&&b.point.series.chart,
|
||||
d=b.seriesOptions;c&&d&&(a?c.addSingleSeriesAsDrilldown(b.point,d):c.addSeriesAsDrilldown(b.point,d))})};f.Axis.prototype.drilldownCategory=function(a,b){var d,c,e=this.getDDPoints(a);for(d in e)(c=e[d])&&c.series&&c.series.visible&&c.doDrilldown&&c.doDrilldown(!0,a,b);this.chart.applyDrilldown()};f.Axis.prototype.getDDPoints=function(a){var b=[];l(this.series,function(d){var c,e=d.xData,f=d.points;for(c=0;c<e.length;c++)if(e[c]===a&&d.options.data[c]&&d.options.data[c].drilldown){b.push(f?f[c]:!0);
|
||||
break}});return b};E.prototype.drillable=function(){var a=this.pos,b=this.label,d=this.axis,c="xAxis"===d.coll&&d.getDDPoints,e=c&&d.getDDPoints(a);c&&(b&&e.length?(b.drillable=!0,b.addClass("highcharts-drilldown-axis-label").on("click",function(b){d.drilldownCategory(a,b)})):b&&b.drillable&&(b.on("click",null),b.removeClass("highcharts-drilldown-axis-label")))};x(E.prototype,"addLabel",function(a){a.call(this);this.drillable()});x(f.Point.prototype,"init",function(a,b,d,c){var e=a.call(this,b,d,
|
||||
c);c=(a=b.xAxis)&&a.ticks[c];e.drilldown&&f.addEvent(e,"click",function(a){b.xAxis&&!1===b.chart.options.drilldown.allowPointDrilldown?b.xAxis.drilldownCategory(e.x,a):e.doDrilldown(void 0,void 0,a)});c&&c.drillable();return e});x(f.Series.prototype,"drawDataLabels",function(a){var b=this.chart.options.drilldown.activeDataLabelStyle,d=this.chart.renderer;a.call(this);l(this.points,function(a){a.drilldown&&a.dataLabel&&("contrast"===b.color&&d.getContrast(a.color||this.color),a.dataLabel.addClass("highcharts-drilldown-data-label"))},
|
||||
this)});var B,w=function(a){a.call(this);l(this.points,function(a){a.drilldown&&a.graphic&&a.graphic.addClass("highcharts-drilldown-point")})};for(B in t)t[B].prototype.supportsDrilldown&&x(t[B].prototype,"drawTracker",w)})(n)});
|
||||
751
static/plugin/chart/highcharts/code/js/modules/drilldown.src.js
Normal file
751
static/plugin/chart/highcharts/code/js/modules/drilldown.src.js
Normal file
@ -0,0 +1,751 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
* 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 = {
|
||||
|
||||
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 = {
|
||||
colorIndex: pick(point.colorIndex, oldSeries.colorIndex)
|
||||
};
|
||||
|
||||
|
||||
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
|
||||
color: point.isNull ? new H.Color(color).setOpacity(0).get() : color,
|
||||
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;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
animateFrom.x += (pick(xAxis.oldPos, xAxis.pos) - xAxis.pos);
|
||||
|
||||
each(this.points, function(point) {
|
||||
var animateTo = point.shapeArgs;
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
|
||||
|
||||
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] && 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;
|
||||
|
||||
|
||||
|
||||
label
|
||||
.addClass('highcharts-drilldown-axis-label')
|
||||
|
||||
.on('click', function(e) {
|
||||
axis.drilldownCategory(pos, e);
|
||||
});
|
||||
|
||||
} else if (label && label.drillable) {
|
||||
|
||||
|
||||
|
||||
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(point.x, e); // #5822, x changed
|
||||
} 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');
|
||||
|
||||
|
||||
}
|
||||
}, 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');
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
for (type in seriesTypes) {
|
||||
if (seriesTypes[type].prototype.supportsDrilldown) {
|
||||
wrap(seriesTypes[type].prototype, 'drawTracker', drawTrackerWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
||||
26
static/plugin/chart/highcharts/code/js/modules/exporting.js
Normal file
26
static/plugin/chart/highcharts/code/js/modules/exporting.js
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
Exporting module
|
||||
|
||||
(c) 2010-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(q){"object"===typeof module&&module.exports?module.exports=q:q(Highcharts)})(function(q){(function(f){var q=f.defaultOptions,v=f.doc,A=f.Chart,x=f.addEvent,F=f.removeEvent,G=f.fireEvent,w=f.createElement,B=f.discardElement,H=f.css,p=f.merge,C=f.pick,u=f.each,y=f.extend,I=f.splat,z=f.win,D=f.SVGRenderer,J=f.Renderer.prototype.symbols;y(q.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"});q.navigation={buttonOptions:{theme:{},symbolSize:14,symbolX:12.5,symbolY:10.5,align:"right",buttonSpacing:3,height:22,verticalAlign:"top",width:24}};q.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,b,d){var e;a=w("form",p({method:"post",action:a,enctype:"multipart/form-data"},d),{display:"none"},v.body);for(e in b)w("input",{type:"hidden",name:e,value:b[e]},
|
||||
null,a);a.submit();B(a)};y(A.prototype,{sanitizeSVG:function(a){return 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 /,'\x3csvg xmlns:xlink\x3d"http://www.w3.org/1999/xlink" ').replace(/ (NS[0-9]+\:)?href=/g," xlink:href\x3d").replace(/\n/," ").replace(/<\/svg>.*?$/,"\x3c/svg\x3e").replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g,
|
||||
'$1\x3d"rgb($2)" $1-opacity\x3d"$3"').replace(/ /g,"\u00a0").replace(/­/g,"\u00ad")},getChartHTML:function(){this.inlineStyles();return this.container.innerHTML},getSVG:function(a){var b=this,d,e,g,E,k,c=p(b.options,a),n=c.exporting.allowHTML;v.createElementNS||(v.createElementNS=function(a,b){return v.createElement(b)});e=w("div",null,{position:"absolute",top:"-9999em",width:b.chartWidth+"px",height:b.chartHeight+"px"},v.body);g=b.renderTo.style.width;k=b.renderTo.style.height;g=c.exporting.sourceWidth||
|
||||
c.chart.width||/px$/.test(g)&&parseInt(g,10)||600;k=c.exporting.sourceHeight||c.chart.height||/px$/.test(k)&&parseInt(k,10)||400;y(c.chart,{animation:!1,renderTo:e,forExport:!0,renderer:"SVGRenderer",width:g,height:k});c.exporting.enabled=!1;delete c.data;c.series=[];u(b.series,function(a){E=p(a.userOptions,{animation:!1,enableMouseTracking:!1,showCheckbox:!1,visible:a.visible});E.isInternal||c.series.push(E)});a&&u(["xAxis","yAxis"],function(b){u(I(a[b]),function(a,e){c[b][e]=p(c[b][e],a)})});d=
|
||||
new f.Chart(c,b.callback);u(["xAxis","yAxis"],function(a){u(b[a],function(b,e){e=d[a][e];var c=b.getExtremes();b=c.userMin;c=c.userMax;!e||void 0===b&&void 0===c||e.setExtremes(b,c,!0,!1)})});g=d.getChartHTML();c=null;d.destroy();B(e);n&&(e=g.match(/<\/svg>(.*?$)/))&&(e='\x3cforeignObject x\x3d"0" y\x3d"0" width\x3d"200" height\x3d"200"\x3e\x3cbody xmlns\x3d"http://www.w3.org/1999/xhtml"\x3e'+e[1]+"\x3c/body\x3e\x3c/foreignObject\x3e",g=g.replace("\x3c/svg\x3e",e+"\x3c/svg\x3e"));g=this.sanitizeSVG(g);
|
||||
return g=g.replace(/(url\(#highcharts-[0-9]+)"/g,"$1").replace(/"/g,"'")},getSVGForExport:function(a,b){var d=this.options.exporting;return this.getSVG(p({chart:{borderRadius:0}},d.chartOptions,b,{exporting:{sourceWidth:a&&a.sourceWidth||d.sourceWidth,sourceHeight:a&&a.sourceHeight||d.sourceHeight}}))},exportChart:function(a,b){b=this.getSVGForExport(a,b);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:b},a.formAttributes)},
|
||||
print:function(){var a=this,b=a.container,d=[],e=b.parentNode,g=v.body,f=g.childNodes,k=a.options.exporting.printMaxWidth,c,n;if(!a.isPrinting){a.isPrinting=!0;a.pointer.reset(null,0);G(a,"beforePrint");if(n=k&&a.chartWidth>k)c=[a.options.chart.width,void 0,!1],a.setSize(k,void 0,!1);u(f,function(a,b){1===a.nodeType&&(d[b]=a.style.display,a.style.display="none")});g.appendChild(b);z.focus();z.print();setTimeout(function(){e.appendChild(b);u(f,function(a,b){1===a.nodeType&&(a.style.display=d[b])});
|
||||
a.isPrinting=!1;n&&a.setSize.apply(a,c);G(a,"afterPrint")},1E3)}},contextMenu:function(a,b,d,e,g,f,k){var c=this,n=c.chartWidth,h=c.chartHeight,l="cache-"+a,m=c[l],r=Math.max(g,f),t,p,q=function(b){c.pointer.inClass(b.target,a)||p()};m||(c[l]=m=w("div",{className:a},{position:"absolute",zIndex:1E3,padding:r+"px"},c.container),t=w("div",{className:"highcharts-menu"},null,m),p=function(){H(m,{display:"none"});k&&k.setState(0);c.openMenu=!1},x(m,"mouseleave",function(){m.hideTimer=setTimeout(p,500)}),
|
||||
x(m,"mouseenter",function(){clearTimeout(m.hideTimer)}),x(v,"mouseup",q),x(c,"destroy",function(){F(v,"mouseup",q)}),u(b,function(a){if(a){var b;b=a.separator?w("hr",null,null,t):w("div",{className:"highcharts-menu-item",onclick:function(b){b&&b.stopPropagation();p();a.onclick&&a.onclick.apply(c,arguments)},innerHTML:a.text||c.options.lang[a.textKey]},null,t);c.exportDivElements.push(b)}}),c.exportDivElements.push(t,m),c.exportMenuWidth=m.offsetWidth,c.exportMenuHeight=m.offsetHeight);b={display:"block"};
|
||||
d+c.exportMenuWidth>n?b.right=n-d-g-r+"px":b.left=d-r+"px";e+f+c.exportMenuHeight>h&&"top"!==k.alignOptions.verticalAlign?b.bottom=h-e-r+"px":b.top=e+f-r+"px";H(m,b);c.openMenu=!0},addButton:function(a){var b=this,d=b.renderer,e=p(b.options.navigation.buttonOptions,a),g=e.onclick,f=e.menuItems,k,c,n=e.symbolSize||12;b.btnCount||(b.btnCount=0);b.exportDivElements||(b.exportDivElements=[],b.exportSVGElements=[]);if(!1!==e.enabled){var h=e.theme,l=h.states,m=l&&l.hover,l=l&&l.select,r;delete h.states;
|
||||
g?r=function(a){a.stopPropagation();g.call(b,a)}:f&&(r=function(){b.contextMenu(c.menuClassName,f,c.translateX,c.translateY,c.width,c.height,c);c.setState(2)});e.text&&e.symbol?h.paddingLeft=C(h.paddingLeft,25):e.text||y(h,{width:e.width,height:e.height,padding:0});c=d.button(e.text,0,0,r,h,m,l).addClass(a.className).attr({title:b.options.lang[e._titleKey],zIndex:3});c.menuClassName=a.menuClassName||"highcharts-menu-"+b.btnCount++;e.symbol&&(k=d.symbol(e.symbol,e.symbolX-n/2,e.symbolY-n/2,n,n).addClass("highcharts-button-symbol").attr({zIndex:1}).add(c));
|
||||
c.add().align(y(e,{width:c.width,x:C(e.x,b.buttonOffset)}),!0,"spacingBox");b.buttonOffset+=(c.width+e.buttonSpacing)*("right"===e.align?-1:1);b.exportSVGElements.push(c,k)}},destroyExport:function(a){var b=a?a.target:this;a=b.exportSVGElements;var d=b.exportDivElements;a&&(u(a,function(a,d){a&&(a.onclick=a.ontouchstart=null,b.exportSVGElements[d]=a.destroy())}),a.length=0);d&&(u(d,function(a,d){clearTimeout(a.hideTimer);F(a,"mouseleave");b.exportDivElements[d]=a.onmouseout=a.onmouseover=a.ontouchstart=
|
||||
a.onclick=null;B(a)}),d.length=0)}});D.prototype.inlineToAttributes="fill stroke strokeLinecap strokeLinejoin strokeWidth textAnchor x y".split(" ");D.prototype.inlineBlacklist=[/-/,/^(clipPath|cssText|d|height|width)$/,/^font$/,/[lL]ogical(Width|Height)$/,/perspective/,/TapHighlightColor/,/^transition/];D.prototype.unstyledElements=["clipPath","defs","desc"];A.prototype.inlineStyles=function(){function a(a){return a.replace(/([A-Z])/g,function(a,b){return"-"+b.toLowerCase()})}function b(d){var h,
|
||||
l,m,r="",t,n;if(1===d.nodeType&&-1===q.indexOf(d.nodeName)){l=z.getComputedStyle(d,null);m="svg"===d.nodeName?{}:z.getComputedStyle(d.parentNode,null);k[d.nodeName]||(c||(c=v.createElementNS(f.SVG_NS,"svg"),c.setAttribute("version","1.1"),v.body.appendChild(c)),t=v.createElementNS(d.namespaceURI,d.nodeName),c.appendChild(t),k[d.nodeName]=p(z.getComputedStyle(t,null)),c.removeChild(t));for(h in l){t=!1;for(n=g.length;n--&&!t;)t=g[n].test(h)||"function"===typeof l[h];t||m[h]!==l[h]&&k[d.nodeName][h]!==
|
||||
l[h]&&(-1!==e.indexOf(h)?d.setAttribute(a(h),l[h]):r+=a(h)+":"+l[h]+";")}r&&(h=d.getAttribute("style"),d.setAttribute("style",(h?h+";":"")+r));"text"!==d.nodeName&&u(d.children||d.childNodes,b)}}var d=this.renderer,e=d.inlineToAttributes,g=d.inlineBlacklist,q=d.unstyledElements,k={},c;b(this.container.querySelector("svg"));c.parentNode.removeChild(c)};J.menu=function(a,b,d,e){return["M",a,b+2.5,"L",a+d,b+2.5,"M",a,b+e/2+.5,"L",a+d,b+e/2+.5,"M",a,b+e-1.5,"L",a+d,b+e-1.5]};A.prototype.renderExporting=
|
||||
function(){var a,b=this.options.exporting,d=b.buttons,e=this.isDirtyExporting||!this.exportSVGElements;this.buttonOffset=0;this.isDirtyExporting&&this.destroyExport();if(e&&!1!==b.enabled){for(a in d)this.addButton(d[a]);this.isDirtyExporting=!1}x(this,"destroy",this.destroyExport)};A.prototype.callbacks.push(function(a){a.renderExporting();x(a,"redraw",a.renderExporting);u(["exporting","navigation"],function(b){a[b]={update:function(d,e){a.isDirtyExporting=!0;p(!0,a.options[b],d);C(e,!0)&&a.redraw()}}})})})(q)});
|
||||
948
static/plugin/chart/highcharts/code/js/modules/exporting.src.js
Normal file
948
static/plugin/chart/highcharts/code/js/modules/exporting.src.js
Normal file
@ -0,0 +1,948 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
* 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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
|
||||
return svg;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return innerHTML of chart. Used as hook for plugins.
|
||||
*/
|
||||
getChartHTML: function() {
|
||||
|
||||
this.inlineStyles();
|
||||
|
||||
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,
|
||||
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);
|
||||
|
||||
|
||||
|
||||
// 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() {
|
||||
menu.hideTimer = setTimeout(hide, 500);
|
||||
});
|
||||
addEvent(menu, 'mouseenter', function() {
|
||||
clearTimeout(menu.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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 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({
|
||||
|
||||
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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
clearTimeout(elem.hideTimer); // #5427
|
||||
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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// These ones are translated to attributes rather than styles
|
||||
SVGRenderer.prototype.inlineToAttributes = [
|
||||
'fill',
|
||||
'stroke',
|
||||
'strokeLinecap',
|
||||
'strokeLinejoin',
|
||||
'strokeWidth',
|
||||
'textAnchor',
|
||||
'x',
|
||||
'y'
|
||||
];
|
||||
// These CSS properties are not inlined. Remember camelCase.
|
||||
SVGRenderer.prototype.inlineBlacklist = [
|
||||
/-/, // In Firefox, both hyphened and camelCased names are listed
|
||||
/^(clipPath|cssText|d|height|width)$/, // Full words
|
||||
/^font$/, // more specific props are set
|
||||
/[lL]ogical(Width|Height)$/,
|
||||
/perspective/,
|
||||
/TapHighlightColor/,
|
||||
/^transition/
|
||||
// /^text (border|color|cursor|height|webkitBorder)/
|
||||
];
|
||||
SVGRenderer.prototype.unstyledElements = [
|
||||
'clipPath',
|
||||
'defs',
|
||||
'desc'
|
||||
];
|
||||
|
||||
/**
|
||||
* Analyze inherited styles from stylesheets and add them inline
|
||||
*
|
||||
* @todo: What are the border styles for text about? In general, text has a lot of properties.
|
||||
* @todo: Make it work with IE9 and IE10.
|
||||
*/
|
||||
Chart.prototype.inlineStyles = function() {
|
||||
var renderer = this.renderer,
|
||||
inlineToAttributes = renderer.inlineToAttributes,
|
||||
blacklist = renderer.inlineBlacklist,
|
||||
unstyledElements = renderer.unstyledElements,
|
||||
defaultStyles = {},
|
||||
dummySVG;
|
||||
|
||||
/**
|
||||
* Make hyphenated property names out of camelCase
|
||||
*/
|
||||
function hyphenate(prop) {
|
||||
return prop.replace(
|
||||
/([A-Z])/g,
|
||||
function(a, b) {
|
||||
return '-' + b.toLowerCase();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this on all elements and recurse to children
|
||||
*/
|
||||
function recurse(node) {
|
||||
var prop,
|
||||
styles,
|
||||
parentStyles,
|
||||
cssText = '',
|
||||
dummy,
|
||||
styleAttr,
|
||||
blacklisted,
|
||||
i;
|
||||
|
||||
if (node.nodeType === 1 && unstyledElements.indexOf(node.nodeName) === -1) {
|
||||
styles = win.getComputedStyle(node, null);
|
||||
parentStyles = node.nodeName === 'svg' ? {} : win.getComputedStyle(node.parentNode, null);
|
||||
|
||||
// Get default styles from the browser so that we don't have to add these
|
||||
if (!defaultStyles[node.nodeName]) {
|
||||
if (!dummySVG) {
|
||||
dummySVG = doc.createElementNS(H.SVG_NS, 'svg');
|
||||
dummySVG.setAttribute('version', '1.1');
|
||||
doc.body.appendChild(dummySVG);
|
||||
}
|
||||
dummy = doc.createElementNS(node.namespaceURI, node.nodeName);
|
||||
dummySVG.appendChild(dummy);
|
||||
defaultStyles[node.nodeName] = merge(win.getComputedStyle(dummy, null)); // Copy, so we can remove the node
|
||||
dummySVG.removeChild(dummy);
|
||||
}
|
||||
|
||||
// Loop over all the computed styles and check whether they are in the
|
||||
// white list for styles or atttributes.
|
||||
for (prop in styles) {
|
||||
|
||||
// Check against blacklist
|
||||
blacklisted = false;
|
||||
i = blacklist.length;
|
||||
while (i-- && !blacklisted) {
|
||||
blacklisted = blacklist[i].test(prop) || typeof styles[prop] === 'function';
|
||||
}
|
||||
|
||||
if (!blacklisted) {
|
||||
|
||||
// If parent node has the same style, it gets inherited, no need to inline it
|
||||
if (parentStyles[prop] !== styles[prop] && defaultStyles[node.nodeName][prop] !== styles[prop]) {
|
||||
|
||||
// Attributes
|
||||
if (inlineToAttributes.indexOf(prop) !== -1) {
|
||||
node.setAttribute(hyphenate(prop), styles[prop]);
|
||||
|
||||
// Styles
|
||||
} else {
|
||||
cssText += hyphenate(prop) + ':' + styles[prop] + ';';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply styles
|
||||
if (cssText) {
|
||||
styleAttr = node.getAttribute('style');
|
||||
node.setAttribute('style', (styleAttr ? styleAttr + ';' : '') + cssText);
|
||||
}
|
||||
|
||||
if (node.nodeName === 'text') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Recurse
|
||||
each(node.children || node.childNodes, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the dummy objects used to get defaults
|
||||
*/
|
||||
function tearDown() {
|
||||
dummySVG.parentNode.removeChild(dummySVG);
|
||||
}
|
||||
|
||||
recurse(this.container.querySelector('svg'));
|
||||
tearDown();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
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
static/plugin/chart/highcharts/code/js/modules/funnel.js
Normal file
12
static/plugin/chart/highcharts/code/js/modules/funnel.js
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
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},{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)});
|
||||
290
static/plugin/chart/highcharts/code/js/modules/funnel.src.js
Normal file
290
static/plugin/chart/highcharts/code/js/modules/funnel.src.js
Normal file
@ -0,0 +1,290 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
* 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
|
||||
|
||||
|
||||
},
|
||||
|
||||
// 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
static/plugin/chart/highcharts/code/js/modules/gantt.js
Normal file
24
static/plugin/chart/highcharts/code/js/modules/gantt.js
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
Gantt series
|
||||
|
||||
(c) 2016 Lars A. V. Cabrera
|
||||
|
||||
--- WORK IN PROGRESS ---
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(m){"object"===typeof module&&module.exports?module.exports=m:m(Highcharts)})(function(m){(function(c){var m=c.dateFormat,p=c.each,q=c.isObject,u=c.pick,l=c.wrap,n=c.Axis,v=c.Chart,r=c.Tick;n.prototype.isOuterAxis=function(){var a=this,f=-1,b=!0;p(this.chart.axes,function(d,g){d.side===a.side&&(d===a?f=g:0<=f&&g>f&&(b=!1))});return b};r.prototype.getLabelWidth=function(){return this.label.getBBox().width};n.prototype.getMaxLabelLength=function(a){var f=this.tickPositions,b=this.ticks,d=0;
|
||||
if(!this.maxLabelLength||a)p(f,function(a){(a=b[a])&&a.labelLength>d&&(d=a.labelLength)}),this.maxLabelLength=d;return this.maxLabelLength};n.prototype.addTitle=function(){var a=this.chart.renderer,f=this.axisParent,b=this.horiz,d=this.opposite,g=this.options,e=g.title,h;this.showAxis=h=this.hasData()||u(g.showEmpty,!0);g.title="";this.axisTitle||((g=e.textAlign)||(g=(b?{low:"left",middle:"center",high:"right"}:{low:d?"right":"left",middle:"center",high:d?"left":"right"})[e.align]),this.axisTitle=
|
||||
a.text(e.text,0,0,e.useHTML).attr({zIndex:7,rotation:e.rotation||0,align:g}).addClass("highcharts-axis-title").add(f),this.axisTitle.isNew=!0);this.axisTitle[h?"show":"hide"](!0)};c.dateFormats={W:function(a){a=new Date(a);var f=0===a.getUTCDay()?7:a.getUTCDay(),b=a.getTime(),d=new Date(a.getUTCFullYear(),0,1,-6);a.setDate(a.getUTCDate()+4-f);return 1+Math.floor(Math.floor((b-d)/864E5)/7)},E:function(a){return m("%a",a,!0).charAt(0)}};l(r.prototype,"addLabel",function(a){var f=this.axis,b=void 0!==
|
||||
f.options.categories,d=f.tickPositions,d=this.pos!==d[d.length-1];(!f.options.grid||b||d)&&a.apply(this)});l(r.prototype,"getLabelPosition",function(a,f,b,d){var g=a.apply(this,Array.prototype.slice.call(arguments,1)),e=this.axis,h=e.options,k=h.tickInterval||1,c,t;h.grid&&(c=h.labels.style.fontSize,t=e.chart.renderer.fontMetrics(c,d),c=t.b,t=t.h,e.horiz&&void 0===h.categories?(h=e.axisGroup.getBBox().height,k=this.pos+k/2,g.x=e.translate(k)+e.left,k=h/2+t/2-Math.abs(t-c),g.y=0===e.side?b-k:b+k):
|
||||
(void 0===h.categories&&(k=this.pos+k/2,g.y=e.translate(k)+e.top+c/2),k=this.getLabelWidth()/2-e.maxLabelLength/2,g.x=3===e.side?g.x+k:g.x-k));return g});l(n.prototype,"tickSize",function(a){var f=a.apply(this,Array.prototype.slice.call(arguments,1)),b;this.options.grid&&!this.horiz&&(b=2*Math.abs(this.defaultLeftAxisOptions.labels.x),this.maxLabelLength||(this.maxLabelLength=this.getMaxLabelLength()),b=this.maxLabelLength+b,f[0]=b);return f});l(n.prototype,"getOffset",function(a){var f=this.chart.axisOffset,
|
||||
b=this.side,d,g,e=this.options,h=e.title,k=h&&h.text&&!1!==h.enabled;this.options.grid&&q(this.options.title)?(g=this.tickSize("tick")[0],f[b]&&g&&(d=f[b]+g),k&&this.addTitle(),a.apply(this,Array.prototype.slice.call(arguments,1)),f[b]=u(d,f[b]),e.title=h):a.apply(this,Array.prototype.slice.call(arguments,1))});l(n.prototype,"renderUnsquish",function(a){this.options.grid&&(this.labelRotation=0,this.options.labels.rotation=0);a.apply(this)});l(n.prototype,"setOptions",function(a,f){f.grid&&this.horiz&&
|
||||
(f.startOnTick=!0,f.minPadding=0,f.endOnTick=!0);a.apply(this,Array.prototype.slice.call(arguments,1))});l(n.prototype,"render",function(a){var f=this.options,b,d,g,e,h,k,c=this.chart.renderer;if(f.grid){if(b=2*Math.abs(this.defaultLeftAxisOptions.labels.x),b=this.maxLabelLength+b,d=f.lineWidth,this.rightWall&&this.rightWall.destroy(),a.apply(this),a=this.axisGroup.getBBox(),this.horiz&&(this.rightWall=c.path(["M",a.x+this.width+1,a.y,"L",a.x+this.width+1,a.y+a.height]).attr({stroke:f.tickColor||
|
||||
"#ccd6eb","stroke-width":f.tickWidth||1,zIndex:7,class:"grid-wall"}).add(this.axisGroup)),this.isOuterAxis()&&this.axisLine&&(this.horiz&&(b=a.height-1),d)){a=this.getLinePath(d);h=a.indexOf("M")+1;k=a.indexOf("L")+1;g=a.indexOf("M")+2;e=a.indexOf("L")+2;if(0===this.side||3===this.side)b=-b;this.horiz?(a[g]+=b,a[e]+=b):(a[h]+=b,a[k]+=b);this.axisLineExtra?this.axisLineExtra.animate({d:a}):this.axisLineExtra=c.path(a).attr({stroke:f.lineColor,"stroke-width":d,zIndex:7}).add(this.axisGroup);this.axisLine[this.showAxis?
|
||||
"show":"hide"](!0)}}else a.apply(this)});l(v.prototype,"render",function(a){var f=25/11,b,c;p(this.axes,function(a){var e=a.options;e.grid&&(c=e.labels.style.fontSize,b=a.chart.renderer.fontMetrics(c),"datetime"===e.type&&(e.units=[["millisecond",[1]],["second",[1]],["minute",[1]],["hour",[1]],["day",[1]],["week",[1]],["month",[1]],["year",null]]),a.horiz?e.tickLength=e.cellHeight||b.h*f:(e.tickWidth=1,e.lineWidth||(e.lineWidth=1)))});a.apply(this)})})(m);(function(c){var m=c.getOptions().plotOptions,
|
||||
p=c.seriesTypes.column,q=c.each,u=c.extendClass,l=c.isNumber,n=c.isObject,v=c.merge,r=c.pick,a=c.seriesTypes,f=c.stop,b=c.wrap,d=c.Axis,g=c.Point,e=c.Series;m.xrange=v(m.column,{tooltip:{pointFormat:'\x3cspan style\x3d"color:{point.color}"\x3e\u25cf\x3c/span\x3e {series.name}: \x3cb\x3e{point.yCategory}\x3c/b\x3e\x3cbr/\x3e'}});a.xrange=u(p,{pointClass:u(g,{getLabelConfig:function(){var a=g.prototype.getLabelConfig.call(this);a.x2=this.x2;a.yCategory=this.yCategory=this.series.yAxis.categories&&this.series.yAxis.categories[this.y];
|
||||
return a}}),type:"xrange",forceDL:!0,parallelArrays:["x","x2","y"],requireSorting:!1,animate:a.line.prototype.animate,getColumnMetrics:function(){function a(){q(f.series,function(a){var b=a.xAxis;a.xAxis=a.yAxis;a.yAxis=b})}var b,f=this.chart;a();this.yAxis.closestPointRange=1;b=p.prototype.getColumnMetrics.call(this);a();return b},cropData:function(a,b,f,c){b=e.prototype.cropData.call(this,this.x2Data,b,f,c);b.xData=a.slice(b.start,b.end);return b},translate:function(){p.prototype.translate.apply(this,
|
||||
arguments);var a=this.xAxis,b=this.columnMetrics,f=this.options.minPointLength||0;q(this.points,function(c){var e=c.plotX,d=r(c.x2,c.x+(c.len||0)),d=a.toPixels(d,!0),g=d-e,h;f&&(h=f-g,0>h&&(h=0),e-=h/2,d+=h/2);e=Math.max(e,-10);d=Math.min(Math.max(d,-10),a.len+10);c.shapeArgs={x:e,y:c.plotY+b.offset,width:d-e,height:b.width};c.tooltipPos[0]+=g/2;c.tooltipPos[1]-=b.width/2;if(d=c.partialFill)n(d)&&(d=d.amount),l(d)||(d=0),e=c.shapeArgs,c.partShapeArgs={x:e.x,y:e.y+1,width:e.width*d,height:e.height-
|
||||
2}})},drawPoints:function(){var a=this,b=this.chart,c=b.renderer,e=b.pointCount<(a.options.animationLimit||250)?"animate":"attr";q(a.points,function(b){var d=b.graphic,g=b.shapeType,h=b.shapeArgs,k=b.partShapeArgs;if(l(b.plotY)&&null!==b.y)if(d){if(f(d),b.graphicOriginal[e](v(h)),k)b.graphicOverlay[e](v(k))}else b.graphic=d=c.g("point").attr({"class":b.getClassName()}).add(b.group||a.group),b.graphicOriginal=c[g](h).addClass("highcharts-partfill-original").add(d),k&&(b.graphicOverlay=c[g](k).addClass("highcharts-partfill-overlay").add(d));
|
||||
else d&&(b.graphic=d.destroy())})}});b(d.prototype,"getSeriesExtremes",function(a){var b=this.series,c,d;a.call(this);this.isXAxis&&"xrange"===b.type&&(c=r(this.dataMax,Number.MIN_VALUE),q(this.series,function(a){q(a.x2Data||[],function(a){a>c&&(c=a,d=!0)})}),d&&(this.dataMax=c))})})(m)});
|
||||
793
static/plugin/chart/highcharts/code/js/modules/gantt.src.js
Normal file
793
static/plugin/chart/highcharts/code/js/modules/gantt.src.js
Normal file
@ -0,0 +1,793 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
* Gantt series
|
||||
*
|
||||
* (c) 2016 Lars A. V. Cabrera
|
||||
*
|
||||
* --- WORK IN PROGRESS ---
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* (c) 2016 Highsoft AS
|
||||
* Authors: Lars A. V. Cabrera
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var dateFormat = H.dateFormat,
|
||||
each = H.each,
|
||||
isObject = H.isObject,
|
||||
pick = H.pick,
|
||||
wrap = H.wrap,
|
||||
Axis = H.Axis,
|
||||
Chart = H.Chart,
|
||||
Tick = H.Tick;
|
||||
|
||||
|
||||
// Enum for which side the axis is on.
|
||||
// Maps to axis.side
|
||||
var axisSide = {
|
||||
top: 0,
|
||||
right: 1,
|
||||
bottom: 2,
|
||||
left: 3,
|
||||
0: 'top',
|
||||
1: 'right',
|
||||
2: 'bottom',
|
||||
3: 'left'
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if an axis is the outer axis in its dimension. Since
|
||||
* axes are placed outwards in order, the axis with the highest
|
||||
* index is the outermost axis.
|
||||
*
|
||||
* Example: If there are multiple x-axes at the top of the chart,
|
||||
* this function returns true if the axis supplied is the last
|
||||
* of the x-axes.
|
||||
*
|
||||
* @return true if the axis is the outermost axis in its dimension;
|
||||
* false if not
|
||||
*/
|
||||
Axis.prototype.isOuterAxis = function() {
|
||||
var axis = this,
|
||||
thisIndex = -1,
|
||||
isOuter = true;
|
||||
|
||||
each(this.chart.axes, function(otherAxis, index) {
|
||||
if (otherAxis.side === axis.side) {
|
||||
if (otherAxis === axis) {
|
||||
// Get the index of the axis in question
|
||||
thisIndex = index;
|
||||
|
||||
// Check thisIndex >= 0 in case thisIndex has
|
||||
// not been found yet
|
||||
} else if (thisIndex >= 0 && index > thisIndex) {
|
||||
// There was an axis on the same side with a
|
||||
// higher index. Exit the loop.
|
||||
isOuter = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
// There were either no other axes on the same side,
|
||||
// or the other axes were not farther from the chart
|
||||
return isOuter;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shortcut function to Tick.label.getBBox().width.
|
||||
*
|
||||
* @return {number} width - the width of the tick label
|
||||
*/
|
||||
Tick.prototype.getLabelWidth = function() {
|
||||
return this.label.getBBox().width;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the maximum label length.
|
||||
* This function can be used in states where the axis.maxLabelLength has not
|
||||
* been set.
|
||||
*
|
||||
* @param {boolean} force - Optional parameter to force a new calculation, even
|
||||
* if a value has already been set
|
||||
* @return {number} maxLabelLength - the maximum label length of the axis
|
||||
*/
|
||||
Axis.prototype.getMaxLabelLength = function(force) {
|
||||
var tickPositions = this.tickPositions,
|
||||
ticks = this.ticks,
|
||||
maxLabelLength = 0;
|
||||
|
||||
if (!this.maxLabelLength || force) {
|
||||
each(tickPositions, function(tick) {
|
||||
tick = ticks[tick];
|
||||
if (tick && tick.labelLength > maxLabelLength) {
|
||||
maxLabelLength = tick.labelLength;
|
||||
}
|
||||
});
|
||||
this.maxLabelLength = maxLabelLength;
|
||||
}
|
||||
return this.maxLabelLength;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the axis defined in axis.options.title
|
||||
*/
|
||||
Axis.prototype.addTitle = function() {
|
||||
var axis = this,
|
||||
renderer = axis.chart.renderer,
|
||||
axisParent = axis.axisParent,
|
||||
horiz = axis.horiz,
|
||||
opposite = axis.opposite,
|
||||
options = axis.options,
|
||||
axisTitleOptions = options.title,
|
||||
hasData,
|
||||
showAxis,
|
||||
textAlign;
|
||||
|
||||
// For reuse in Axis.render
|
||||
hasData = axis.hasData();
|
||||
axis.showAxis = showAxis = hasData || pick(options.showEmpty, true);
|
||||
|
||||
// Disregard title generation in original Axis.getOffset()
|
||||
options.title = '';
|
||||
|
||||
if (!axis.axisTitle) {
|
||||
textAlign = axisTitleOptions.textAlign;
|
||||
if (!textAlign) {
|
||||
textAlign = (horiz ? {
|
||||
low: 'left',
|
||||
middle: 'center',
|
||||
high: 'right'
|
||||
} : {
|
||||
low: opposite ? 'right' : 'left',
|
||||
middle: 'center',
|
||||
high: opposite ? 'left' : 'right'
|
||||
})[axisTitleOptions.align];
|
||||
}
|
||||
axis.axisTitle = renderer.text(
|
||||
axisTitleOptions.text,
|
||||
0,
|
||||
0,
|
||||
axisTitleOptions.useHTML
|
||||
)
|
||||
.attr({
|
||||
zIndex: 7,
|
||||
rotation: axisTitleOptions.rotation || 0,
|
||||
align: textAlign
|
||||
})
|
||||
.addClass('highcharts-axis-title')
|
||||
|
||||
// Add to axisParent instead of axisGroup, to ignore the space
|
||||
// it takes
|
||||
.add(axisParent);
|
||||
axis.axisTitle.isNew = true;
|
||||
}
|
||||
|
||||
|
||||
// hide or show the title depending on whether showEmpty is set
|
||||
axis.axisTitle[showAxis ? 'show' : 'hide'](true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add custom date formats
|
||||
*/
|
||||
H.dateFormats = {
|
||||
// Week number
|
||||
W: function(timestamp) {
|
||||
var date = new Date(timestamp),
|
||||
day = date.getUTCDay() === 0 ? 7 : date.getUTCDay(),
|
||||
time = date.getTime(),
|
||||
startOfYear = new Date(date.getUTCFullYear(), 0, 1, -6),
|
||||
dayNumber;
|
||||
date.setDate(date.getUTCDate() + 4 - day);
|
||||
dayNumber = Math.floor((time - startOfYear) / 86400000);
|
||||
return 1 + Math.floor(dayNumber / 7);
|
||||
},
|
||||
// First letter of the day of the week, e.g. 'M' for 'Monday'.
|
||||
E: function(timestamp) {
|
||||
return dateFormat('%a', timestamp, true).charAt(0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevents adding the last tick label if the axis is not a category axis.
|
||||
*
|
||||
* Since numeric labels are normally placed at starts and ends of a range of
|
||||
* value, and this module makes the label point at the value, an "extra" label
|
||||
* would appear.
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*/
|
||||
wrap(Tick.prototype, 'addLabel', function(proceed) {
|
||||
var axis = this.axis,
|
||||
isCategoryAxis = axis.options.categories !== undefined,
|
||||
tickPositions = axis.tickPositions,
|
||||
lastTick = tickPositions[tickPositions.length - 1],
|
||||
isLastTick = this.pos !== lastTick;
|
||||
|
||||
if (!axis.options.grid || isCategoryAxis || isLastTick) {
|
||||
proceed.apply(this);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Center tick labels vertically and horizontally between ticks
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*
|
||||
* @return {object} object - an object containing x and y positions
|
||||
* for the tick
|
||||
*/
|
||||
wrap(Tick.prototype, 'getLabelPosition', function(proceed, x, y, label) {
|
||||
var retVal = proceed.apply(this, Array.prototype.slice.call(arguments, 1)),
|
||||
axis = this.axis,
|
||||
options = axis.options,
|
||||
tickInterval = options.tickInterval || 1,
|
||||
newX,
|
||||
newPos,
|
||||
axisHeight,
|
||||
fontSize,
|
||||
labelMetrics,
|
||||
lblB,
|
||||
lblH,
|
||||
labelCenter;
|
||||
|
||||
// Only center tick labels if axis has option grid: true
|
||||
if (options.grid) {
|
||||
fontSize = options.labels.style.fontSize;
|
||||
labelMetrics = axis.chart.renderer.fontMetrics(fontSize, label);
|
||||
lblB = labelMetrics.b;
|
||||
lblH = labelMetrics.h;
|
||||
|
||||
if (axis.horiz && options.categories === undefined) {
|
||||
// Center x position
|
||||
axisHeight = axis.axisGroup.getBBox().height;
|
||||
newPos = this.pos + tickInterval / 2;
|
||||
retVal.x = axis.translate(newPos) + axis.left;
|
||||
labelCenter = (axisHeight / 2) + (lblH / 2) - Math.abs(lblH - lblB);
|
||||
|
||||
// Center y position
|
||||
if (axis.side === axisSide.top) {
|
||||
retVal.y = y - labelCenter;
|
||||
} else {
|
||||
retVal.y = y + labelCenter;
|
||||
}
|
||||
} else {
|
||||
// Center y position
|
||||
if (options.categories === undefined) {
|
||||
newPos = this.pos + (tickInterval / 2);
|
||||
retVal.y = axis.translate(newPos) + axis.top + (lblB / 2);
|
||||
}
|
||||
|
||||
// Center x position
|
||||
newX = (this.getLabelWidth() / 2) - (axis.maxLabelLength / 2);
|
||||
if (axis.side === axisSide.left) {
|
||||
retVal.x += newX;
|
||||
} else {
|
||||
retVal.x -= newX;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Draw vertical ticks extra long to create cell floors and roofs.
|
||||
* Overrides the tickLength for vertical axes.
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
* @returns {array} retVal -
|
||||
*/
|
||||
wrap(Axis.prototype, 'tickSize', function(proceed) {
|
||||
var axis = this,
|
||||
retVal = proceed.apply(axis, Array.prototype.slice.call(arguments, 1)),
|
||||
labelPadding,
|
||||
distance;
|
||||
|
||||
if (axis.options.grid && !axis.horiz) {
|
||||
labelPadding = (Math.abs(axis.defaultLeftAxisOptions.labels.x) * 2);
|
||||
if (!axis.maxLabelLength) {
|
||||
axis.maxLabelLength = axis.getMaxLabelLength();
|
||||
}
|
||||
distance = axis.maxLabelLength + labelPadding;
|
||||
|
||||
retVal[0] = distance;
|
||||
}
|
||||
return retVal;
|
||||
});
|
||||
|
||||
/**
|
||||
* Disregards space required by axisTitle, by adding axisTitle to axisParent
|
||||
* instead of axisGroup, and disregarding margins and offsets related to
|
||||
* axisTitle.
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*/
|
||||
wrap(Axis.prototype, 'getOffset', function(proceed) {
|
||||
var axis = this,
|
||||
axisOffset = axis.chart.axisOffset,
|
||||
side = axis.side,
|
||||
axisHeight,
|
||||
tickSize,
|
||||
options = axis.options,
|
||||
axisTitleOptions = options.title,
|
||||
addTitle = axisTitleOptions &&
|
||||
axisTitleOptions.text &&
|
||||
axisTitleOptions.enabled !== false;
|
||||
|
||||
if (axis.options.grid && isObject(axis.options.title)) {
|
||||
|
||||
tickSize = axis.tickSize('tick')[0];
|
||||
if (axisOffset[side] && tickSize) {
|
||||
axisHeight = axisOffset[side] + tickSize;
|
||||
}
|
||||
|
||||
if (addTitle) {
|
||||
// Use the custom addTitle() to add it, while preventing making room
|
||||
// for it
|
||||
axis.addTitle();
|
||||
}
|
||||
|
||||
proceed.apply(axis, Array.prototype.slice.call(arguments, 1));
|
||||
|
||||
axisOffset[side] = pick(axisHeight, axisOffset[side]);
|
||||
|
||||
|
||||
// Put axis options back after original Axis.getOffset() has been called
|
||||
options.title = axisTitleOptions;
|
||||
|
||||
} else {
|
||||
proceed.apply(axis, Array.prototype.slice.call(arguments, 1));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Prevents rotation of labels when squished, as rotating them would not
|
||||
* help.
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*/
|
||||
wrap(Axis.prototype, 'renderUnsquish', function(proceed) {
|
||||
if (this.options.grid) {
|
||||
this.labelRotation = 0;
|
||||
this.options.labels.rotation = 0;
|
||||
}
|
||||
proceed.apply(this);
|
||||
});
|
||||
|
||||
/**
|
||||
* Places leftmost tick at the start of the axis, to create a left wall.
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*/
|
||||
wrap(Axis.prototype, 'setOptions', function(proceed, userOptions) {
|
||||
var axis = this;
|
||||
if (userOptions.grid && axis.horiz) {
|
||||
userOptions.startOnTick = true;
|
||||
userOptions.minPadding = 0;
|
||||
userOptions.endOnTick = true;
|
||||
}
|
||||
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
});
|
||||
|
||||
/**
|
||||
* Draw an extra line on the far side of the the axisLine,
|
||||
* creating cell roofs of a grid.
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*/
|
||||
wrap(Axis.prototype, 'render', function(proceed) {
|
||||
var axis = this,
|
||||
options = axis.options,
|
||||
labelPadding,
|
||||
distance,
|
||||
lineWidth,
|
||||
linePath,
|
||||
yStartIndex,
|
||||
yEndIndex,
|
||||
xStartIndex,
|
||||
xEndIndex,
|
||||
renderer = axis.chart.renderer,
|
||||
axisGroupBox;
|
||||
|
||||
if (options.grid) {
|
||||
labelPadding = (Math.abs(axis.defaultLeftAxisOptions.labels.x) * 2);
|
||||
distance = axis.maxLabelLength + labelPadding;
|
||||
lineWidth = options.lineWidth;
|
||||
|
||||
// Remove right wall before rendering
|
||||
if (axis.rightWall) {
|
||||
axis.rightWall.destroy();
|
||||
}
|
||||
|
||||
// Call original Axis.render() to obtain axis.axisLine and
|
||||
// axis.axisGroup
|
||||
proceed.apply(axis);
|
||||
|
||||
axisGroupBox = axis.axisGroup.getBBox();
|
||||
|
||||
// Add right wall on horizontal axes
|
||||
if (axis.horiz) {
|
||||
axis.rightWall = renderer.path([
|
||||
'M',
|
||||
axisGroupBox.x + axis.width + 1, // account for left wall
|
||||
axisGroupBox.y,
|
||||
'L',
|
||||
axisGroupBox.x + axis.width + 1, // account for left wall
|
||||
axisGroupBox.y + axisGroupBox.height
|
||||
])
|
||||
.attr({
|
||||
stroke: options.tickColor || '#ccd6eb',
|
||||
'stroke-width': options.tickWidth || 1,
|
||||
zIndex: 7,
|
||||
class: 'grid-wall'
|
||||
})
|
||||
.add(axis.axisGroup);
|
||||
}
|
||||
|
||||
if (axis.isOuterAxis() && axis.axisLine) {
|
||||
if (axis.horiz) {
|
||||
// -1 to avoid adding distance each time the chart updates
|
||||
distance = axisGroupBox.height - 1;
|
||||
}
|
||||
|
||||
if (lineWidth) {
|
||||
linePath = axis.getLinePath(lineWidth);
|
||||
xStartIndex = linePath.indexOf('M') + 1;
|
||||
xEndIndex = linePath.indexOf('L') + 1;
|
||||
yStartIndex = linePath.indexOf('M') + 2;
|
||||
yEndIndex = linePath.indexOf('L') + 2;
|
||||
|
||||
// Negate distance if top or left axis
|
||||
if (axis.side === axisSide.top || axis.side === axisSide.left) {
|
||||
distance = -distance;
|
||||
}
|
||||
|
||||
// If axis is horizontal, reposition line path vertically
|
||||
if (axis.horiz) {
|
||||
linePath[yStartIndex] = linePath[yStartIndex] + distance;
|
||||
linePath[yEndIndex] = linePath[yEndIndex] + distance;
|
||||
} else {
|
||||
// If axis is vertical, reposition line path horizontally
|
||||
linePath[xStartIndex] = linePath[xStartIndex] + distance;
|
||||
linePath[xEndIndex] = linePath[xEndIndex] + distance;
|
||||
}
|
||||
|
||||
if (!axis.axisLineExtra) {
|
||||
axis.axisLineExtra = renderer.path(linePath)
|
||||
.attr({
|
||||
stroke: options.lineColor,
|
||||
'stroke-width': lineWidth,
|
||||
zIndex: 7
|
||||
})
|
||||
.add(axis.axisGroup);
|
||||
} else {
|
||||
axis.axisLineExtra.animate({
|
||||
d: linePath
|
||||
});
|
||||
}
|
||||
|
||||
// show or hide the line depending on options.showEmpty
|
||||
axis.axisLine[axis.showAxis ? 'show' : 'hide'](true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
proceed.apply(axis);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Wraps chart rendering with the following customizations:
|
||||
* 1. Prohibit timespans of multitudes of a time unit
|
||||
* 2. Draw cell walls on vertical axes
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*/
|
||||
wrap(Chart.prototype, 'render', function(proceed) {
|
||||
// 25 is optimal height for default fontSize (11px)
|
||||
// 25 / 11 ≈ 2.28
|
||||
var fontSizeToCellHeightRatio = 25 / 11,
|
||||
fontMetrics,
|
||||
fontSize;
|
||||
|
||||
each(this.axes, function(axis) {
|
||||
var options = axis.options;
|
||||
if (options.grid) {
|
||||
fontSize = options.labels.style.fontSize;
|
||||
fontMetrics = axis.chart.renderer.fontMetrics(fontSize);
|
||||
|
||||
// Prohibit timespans of multitudes of a time unit,
|
||||
// e.g. two days, three weeks, etc.
|
||||
if (options.type === 'datetime') {
|
||||
options.units = [
|
||||
['millisecond', [1]],
|
||||
['second', [1]],
|
||||
['minute', [1]],
|
||||
['hour', [1]],
|
||||
['day', [1]],
|
||||
['week', [1]],
|
||||
['month', [1]],
|
||||
['year', null]
|
||||
];
|
||||
}
|
||||
|
||||
// Make tick marks taller, creating cell walls of a grid.
|
||||
// Use cellHeight axis option if set
|
||||
if (axis.horiz) {
|
||||
options.tickLength = options.cellHeight ||
|
||||
fontMetrics.h * fontSizeToCellHeightRatio;
|
||||
} else {
|
||||
options.tickWidth = 1;
|
||||
if (!options.lineWidth) {
|
||||
options.lineWidth = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Call original Chart.render()
|
||||
proceed.apply(this);
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
(function(H) {
|
||||
/**
|
||||
* (c) 2014-2016 Highsoft AS
|
||||
* Authors: Torstein Honsi, Lars A. V. Cabrera
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var defaultPlotOptions = H.getOptions().plotOptions,
|
||||
color = H.Color,
|
||||
columnType = H.seriesTypes.column,
|
||||
each = H.each,
|
||||
extendClass = H.extendClass,
|
||||
isNumber = H.isNumber,
|
||||
isObject = H.isObject,
|
||||
merge = H.merge,
|
||||
pick = H.pick,
|
||||
seriesTypes = H.seriesTypes,
|
||||
stop = H.stop,
|
||||
wrap = H.wrap,
|
||||
Axis = H.Axis,
|
||||
Point = H.Point,
|
||||
Series = H.Series,
|
||||
pointFormat = '<span style="color:{point.color}">' +
|
||||
'\u25CF' +
|
||||
'</span> {series.name}: <b>{point.yCategory}</b><br/>',
|
||||
xrange = 'xrange';
|
||||
|
||||
defaultPlotOptions.xrange = merge(defaultPlotOptions.column, {
|
||||
tooltip: {
|
||||
pointFormat: pointFormat
|
||||
}
|
||||
});
|
||||
seriesTypes.xrange = extendClass(columnType, {
|
||||
pointClass: extendClass(Point, {
|
||||
// Add x2 and yCategory to the available properties for tooltip formats
|
||||
getLabelConfig: function() {
|
||||
var cfg = Point.prototype.getLabelConfig.call(this);
|
||||
|
||||
cfg.x2 = this.x2;
|
||||
cfg.yCategory = this.yCategory = this.series.yAxis.categories && this.series.yAxis.categories[this.y];
|
||||
return cfg;
|
||||
}
|
||||
}),
|
||||
type: xrange,
|
||||
forceDL: true,
|
||||
parallelArrays: ['x', 'x2', 'y'],
|
||||
requireSorting: false,
|
||||
animate: seriesTypes.line.prototype.animate,
|
||||
|
||||
/**
|
||||
* Borrow the column series metrics, but with swapped axes. This gives free access
|
||||
* to features like groupPadding, grouping, pointWidth etc.
|
||||
*/
|
||||
getColumnMetrics: function() {
|
||||
var metrics,
|
||||
chart = this.chart;
|
||||
|
||||
function swapAxes() {
|
||||
each(chart.series, function(s) {
|
||||
var xAxis = s.xAxis;
|
||||
s.xAxis = s.yAxis;
|
||||
s.yAxis = xAxis;
|
||||
});
|
||||
}
|
||||
|
||||
swapAxes();
|
||||
|
||||
this.yAxis.closestPointRange = 1;
|
||||
metrics = columnType.prototype.getColumnMetrics.call(this);
|
||||
|
||||
swapAxes();
|
||||
|
||||
return metrics;
|
||||
},
|
||||
|
||||
/**
|
||||
* Override cropData to show a point where x is outside visible range
|
||||
* but x2 is outside.
|
||||
*/
|
||||
cropData: function(xData, yData, min, max) {
|
||||
|
||||
// Replace xData with x2Data to find the appropriate cropStart
|
||||
var cropData = Series.prototype.cropData,
|
||||
crop = cropData.call(this, this.x2Data, yData, min, max);
|
||||
|
||||
// Re-insert the cropped xData
|
||||
crop.xData = xData.slice(crop.start, crop.end);
|
||||
|
||||
return crop;
|
||||
},
|
||||
|
||||
translate: function() {
|
||||
columnType.prototype.translate.apply(this, arguments);
|
||||
var series = this,
|
||||
xAxis = series.xAxis,
|
||||
metrics = series.columnMetrics,
|
||||
minPointLength = series.options.minPointLength || 0;
|
||||
|
||||
each(series.points, function(point) {
|
||||
var plotX = point.plotX,
|
||||
posX = pick(point.x2, point.x + (point.len || 0)),
|
||||
plotX2 = xAxis.toPixels(posX, true),
|
||||
width = plotX2 - plotX,
|
||||
widthDifference,
|
||||
shapeArgs,
|
||||
partialFill;
|
||||
|
||||
if (minPointLength) {
|
||||
widthDifference = minPointLength - width;
|
||||
if (widthDifference < 0) {
|
||||
widthDifference = 0;
|
||||
}
|
||||
plotX -= widthDifference / 2;
|
||||
plotX2 += widthDifference / 2;
|
||||
}
|
||||
|
||||
plotX = Math.max(plotX, -10);
|
||||
plotX2 = Math.min(Math.max(plotX2, -10), xAxis.len + 10);
|
||||
|
||||
point.shapeArgs = {
|
||||
x: plotX,
|
||||
y: point.plotY + metrics.offset,
|
||||
width: plotX2 - plotX,
|
||||
height: metrics.width
|
||||
};
|
||||
point.tooltipPos[0] += width / 2;
|
||||
point.tooltipPos[1] -= metrics.width / 2;
|
||||
|
||||
// Add a partShapeArgs to the point, based on the shapeArgs property
|
||||
partialFill = point.partialFill;
|
||||
if (partialFill) {
|
||||
// Get the partial fill amount
|
||||
if (isObject(partialFill)) {
|
||||
partialFill = partialFill.amount;
|
||||
}
|
||||
// If it was not a number, assume 0
|
||||
if (!isNumber(partialFill)) {
|
||||
partialFill = 0;
|
||||
}
|
||||
shapeArgs = point.shapeArgs;
|
||||
point.partShapeArgs = {
|
||||
x: shapeArgs.x,
|
||||
y: shapeArgs.y + 1,
|
||||
width: shapeArgs.width * partialFill,
|
||||
height: shapeArgs.height - 2
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
drawPoints: function() {
|
||||
var series = this,
|
||||
chart = this.chart,
|
||||
options = series.options,
|
||||
renderer = chart.renderer,
|
||||
animationLimit = options.animationLimit || 250,
|
||||
verb = chart.pointCount < animationLimit ? 'animate' : 'attr';
|
||||
|
||||
// draw the columns
|
||||
each(series.points, function(point) {
|
||||
var plotY = point.plotY,
|
||||
graphic = point.graphic,
|
||||
type = point.shapeType,
|
||||
shapeArgs = point.shapeArgs,
|
||||
partShapeArgs = point.partShapeArgs,
|
||||
seriesOpts = series.options,
|
||||
pfOptions = point.partialFill,
|
||||
fill,
|
||||
state = point.selected && 'select',
|
||||
cutOff = options.stacking && !options.borderRadius;
|
||||
|
||||
if (isNumber(plotY) && point.y !== null) {
|
||||
if (graphic) { // update
|
||||
stop(graphic);
|
||||
point.graphicOriginal[verb](
|
||||
merge(shapeArgs)
|
||||
);
|
||||
if (partShapeArgs) {
|
||||
point.graphicOverlay[verb](
|
||||
merge(partShapeArgs)
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
point.graphic = graphic = renderer.g('point')
|
||||
.attr({
|
||||
'class': point.getClassName()
|
||||
})
|
||||
.add(point.group || series.group);
|
||||
|
||||
point.graphicOriginal = renderer[type](shapeArgs)
|
||||
.addClass('highcharts-partfill-original')
|
||||
.add(graphic);
|
||||
if (partShapeArgs) {
|
||||
point.graphicOverlay = renderer[type](partShapeArgs)
|
||||
.addClass('highcharts-partfill-overlay')
|
||||
.add(graphic);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else if (graphic) {
|
||||
point.graphic = graphic.destroy(); // #1269
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Max x2 should be considered in xAxis extremes
|
||||
*/
|
||||
wrap(Axis.prototype, 'getSeriesExtremes', function(proceed) {
|
||||
var axis = this,
|
||||
series = axis.series,
|
||||
dataMax,
|
||||
modMax;
|
||||
|
||||
proceed.call(this);
|
||||
if (axis.isXAxis && series.type === xrange) {
|
||||
dataMax = pick(axis.dataMax, Number.MIN_VALUE);
|
||||
each(this.series, function(series) {
|
||||
each(series.x2Data || [], function(val) {
|
||||
if (val > dataMax) {
|
||||
dataMax = val;
|
||||
modMax = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
if (modMax) {
|
||||
axis.dataMax = dataMax;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
(function(H) {
|
||||
/**
|
||||
* (c) 2016 Highsoft AS
|
||||
* Authors: Lars A. V. Cabrera
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
//
|
||||
}(Highcharts));
|
||||
}));
|
||||
19
static/plugin/chart/highcharts/code/js/modules/grid-axis.js
Normal file
19
static/plugin/chart/highcharts/code/js/modules/grid-axis.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
GridAxis
|
||||
|
||||
(c) 2016 Lars A. V. Cabrera
|
||||
|
||||
--- WORK IN PROGRESS ---
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(n){"object"===typeof module&&module.exports?module.exports=n:n(Highcharts)})(function(n){(function(h){var n=h.dateFormat,p=h.each,t=h.isObject,r=h.pick,l=h.wrap,m=h.Axis,u=h.Chart,q=h.Tick;m.prototype.isOuterAxis=function(){var a=this,c=-1,b=!0;p(this.chart.axes,function(e,f){e.side===a.side&&(e===a?c=f:0<=c&&f>c&&(b=!1))});return b};q.prototype.getLabelWidth=function(){return this.label.getBBox().width};m.prototype.getMaxLabelLength=function(a){var c=this.tickPositions,b=this.ticks,e=0;
|
||||
if(!this.maxLabelLength||a)p(c,function(a){(a=b[a])&&a.labelLength>e&&(e=a.labelLength)}),this.maxLabelLength=e;return this.maxLabelLength};m.prototype.addTitle=function(){var a=this.chart.renderer,c=this.axisParent,b=this.horiz,e=this.opposite,f=this.options,d=f.title,g;this.showAxis=g=this.hasData()||r(f.showEmpty,!0);f.title="";this.axisTitle||((f=d.textAlign)||(f=(b?{low:"left",middle:"center",high:"right"}:{low:e?"right":"left",middle:"center",high:e?"left":"right"})[d.align]),this.axisTitle=
|
||||
a.text(d.text,0,0,d.useHTML).attr({zIndex:7,rotation:d.rotation||0,align:f}).addClass("highcharts-axis-title").add(c),this.axisTitle.isNew=!0);this.axisTitle[g?"show":"hide"](!0)};h.dateFormats={W:function(a){a=new Date(a);var c=0===a.getUTCDay()?7:a.getUTCDay(),b=a.getTime(),e=new Date(a.getUTCFullYear(),0,1,-6);a.setDate(a.getUTCDate()+4-c);return 1+Math.floor(Math.floor((b-e)/864E5)/7)},E:function(a){return n("%a",a,!0).charAt(0)}};l(q.prototype,"addLabel",function(a){var c=this.axis,b=void 0!==
|
||||
c.options.categories,e=c.tickPositions,e=this.pos!==e[e.length-1];(!c.options.grid||b||e)&&a.apply(this)});l(q.prototype,"getLabelPosition",function(a,c,b,e){var f=a.apply(this,Array.prototype.slice.call(arguments,1)),d=this.axis,g=d.options,k=g.tickInterval||1,h,l;g.grid&&(h=g.labels.style.fontSize,l=d.chart.renderer.fontMetrics(h,e),h=l.b,l=l.h,d.horiz&&void 0===g.categories?(g=d.axisGroup.getBBox().height,k=this.pos+k/2,f.x=d.translate(k)+d.left,k=g/2+l/2-Math.abs(l-h),f.y=0===d.side?b-k:b+k):
|
||||
(void 0===g.categories&&(k=this.pos+k/2,f.y=d.translate(k)+d.top+h/2),k=this.getLabelWidth()/2-d.maxLabelLength/2,f.x=3===d.side?f.x+k:f.x-k));return f});l(m.prototype,"tickSize",function(a){var c=a.apply(this,Array.prototype.slice.call(arguments,1)),b;this.options.grid&&!this.horiz&&(b=2*Math.abs(this.defaultLeftAxisOptions.labels.x),this.maxLabelLength||(this.maxLabelLength=this.getMaxLabelLength()),b=this.maxLabelLength+b,c[0]=b);return c});l(m.prototype,"getOffset",function(a){var c=this.chart.axisOffset,
|
||||
b=this.side,e,f,d=this.options,g=d.title,k=g&&g.text&&!1!==g.enabled;this.options.grid&&t(this.options.title)?(f=this.tickSize("tick")[0],c[b]&&f&&(e=c[b]+f),k&&this.addTitle(),a.apply(this,Array.prototype.slice.call(arguments,1)),c[b]=r(e,c[b]),d.title=g):a.apply(this,Array.prototype.slice.call(arguments,1))});l(m.prototype,"renderUnsquish",function(a){this.options.grid&&(this.labelRotation=0,this.options.labels.rotation=0);a.apply(this)});l(m.prototype,"setOptions",function(a,c){c.grid&&this.horiz&&
|
||||
(c.startOnTick=!0,c.minPadding=0,c.endOnTick=!0);a.apply(this,Array.prototype.slice.call(arguments,1))});l(m.prototype,"render",function(a){var c=this.options,b,e,f,d,g,k,h=this.chart.renderer;if(c.grid){if(b=2*Math.abs(this.defaultLeftAxisOptions.labels.x),b=this.maxLabelLength+b,e=c.lineWidth,this.rightWall&&this.rightWall.destroy(),a.apply(this),a=this.axisGroup.getBBox(),this.horiz&&(this.rightWall=h.path(["M",a.x+this.width+1,a.y,"L",a.x+this.width+1,a.y+a.height]).attr({stroke:c.tickColor||
|
||||
"#ccd6eb","stroke-width":c.tickWidth||1,zIndex:7,class:"grid-wall"}).add(this.axisGroup)),this.isOuterAxis()&&this.axisLine&&(this.horiz&&(b=a.height-1),e)){a=this.getLinePath(e);g=a.indexOf("M")+1;k=a.indexOf("L")+1;f=a.indexOf("M")+2;d=a.indexOf("L")+2;if(0===this.side||3===this.side)b=-b;this.horiz?(a[f]+=b,a[d]+=b):(a[g]+=b,a[k]+=b);this.axisLineExtra?this.axisLineExtra.animate({d:a}):this.axisLineExtra=h.path(a).attr({stroke:c.lineColor,"stroke-width":e,zIndex:7}).add(this.axisGroup);this.axisLine[this.showAxis?
|
||||
"show":"hide"](!0)}}else a.apply(this)});l(u.prototype,"render",function(a){var c=25/11,b,e;p(this.axes,function(a){var d=a.options;d.grid&&(e=d.labels.style.fontSize,b=a.chart.renderer.fontMetrics(e),"datetime"===d.type&&(d.units=[["millisecond",[1]],["second",[1]],["minute",[1]],["hour",[1]],["day",[1]],["week",[1]],["month",[1]],["year",null]]),a.horiz?d.tickLength=d.cellHeight||b.h*c:(d.tickWidth=1,d.lineWidth||(d.lineWidth=1)))});a.apply(this)})})(n)});
|
||||
545
static/plugin/chart/highcharts/code/js/modules/grid-axis.src.js
Normal file
545
static/plugin/chart/highcharts/code/js/modules/grid-axis.src.js
Normal file
@ -0,0 +1,545 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
* GridAxis
|
||||
*
|
||||
* (c) 2016 Lars A. V. Cabrera
|
||||
*
|
||||
* --- WORK IN PROGRESS ---
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
(function(factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory;
|
||||
} else {
|
||||
factory(Highcharts);
|
||||
}
|
||||
}(function(Highcharts) {
|
||||
(function(H) {
|
||||
/**
|
||||
* (c) 2016 Highsoft AS
|
||||
* Authors: Lars A. V. Cabrera
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var dateFormat = H.dateFormat,
|
||||
each = H.each,
|
||||
isObject = H.isObject,
|
||||
pick = H.pick,
|
||||
wrap = H.wrap,
|
||||
Axis = H.Axis,
|
||||
Chart = H.Chart,
|
||||
Tick = H.Tick;
|
||||
|
||||
|
||||
// Enum for which side the axis is on.
|
||||
// Maps to axis.side
|
||||
var axisSide = {
|
||||
top: 0,
|
||||
right: 1,
|
||||
bottom: 2,
|
||||
left: 3,
|
||||
0: 'top',
|
||||
1: 'right',
|
||||
2: 'bottom',
|
||||
3: 'left'
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if an axis is the outer axis in its dimension. Since
|
||||
* axes are placed outwards in order, the axis with the highest
|
||||
* index is the outermost axis.
|
||||
*
|
||||
* Example: If there are multiple x-axes at the top of the chart,
|
||||
* this function returns true if the axis supplied is the last
|
||||
* of the x-axes.
|
||||
*
|
||||
* @return true if the axis is the outermost axis in its dimension;
|
||||
* false if not
|
||||
*/
|
||||
Axis.prototype.isOuterAxis = function() {
|
||||
var axis = this,
|
||||
thisIndex = -1,
|
||||
isOuter = true;
|
||||
|
||||
each(this.chart.axes, function(otherAxis, index) {
|
||||
if (otherAxis.side === axis.side) {
|
||||
if (otherAxis === axis) {
|
||||
// Get the index of the axis in question
|
||||
thisIndex = index;
|
||||
|
||||
// Check thisIndex >= 0 in case thisIndex has
|
||||
// not been found yet
|
||||
} else if (thisIndex >= 0 && index > thisIndex) {
|
||||
// There was an axis on the same side with a
|
||||
// higher index. Exit the loop.
|
||||
isOuter = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
// There were either no other axes on the same side,
|
||||
// or the other axes were not farther from the chart
|
||||
return isOuter;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shortcut function to Tick.label.getBBox().width.
|
||||
*
|
||||
* @return {number} width - the width of the tick label
|
||||
*/
|
||||
Tick.prototype.getLabelWidth = function() {
|
||||
return this.label.getBBox().width;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the maximum label length.
|
||||
* This function can be used in states where the axis.maxLabelLength has not
|
||||
* been set.
|
||||
*
|
||||
* @param {boolean} force - Optional parameter to force a new calculation, even
|
||||
* if a value has already been set
|
||||
* @return {number} maxLabelLength - the maximum label length of the axis
|
||||
*/
|
||||
Axis.prototype.getMaxLabelLength = function(force) {
|
||||
var tickPositions = this.tickPositions,
|
||||
ticks = this.ticks,
|
||||
maxLabelLength = 0;
|
||||
|
||||
if (!this.maxLabelLength || force) {
|
||||
each(tickPositions, function(tick) {
|
||||
tick = ticks[tick];
|
||||
if (tick && tick.labelLength > maxLabelLength) {
|
||||
maxLabelLength = tick.labelLength;
|
||||
}
|
||||
});
|
||||
this.maxLabelLength = maxLabelLength;
|
||||
}
|
||||
return this.maxLabelLength;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the axis defined in axis.options.title
|
||||
*/
|
||||
Axis.prototype.addTitle = function() {
|
||||
var axis = this,
|
||||
renderer = axis.chart.renderer,
|
||||
axisParent = axis.axisParent,
|
||||
horiz = axis.horiz,
|
||||
opposite = axis.opposite,
|
||||
options = axis.options,
|
||||
axisTitleOptions = options.title,
|
||||
hasData,
|
||||
showAxis,
|
||||
textAlign;
|
||||
|
||||
// For reuse in Axis.render
|
||||
hasData = axis.hasData();
|
||||
axis.showAxis = showAxis = hasData || pick(options.showEmpty, true);
|
||||
|
||||
// Disregard title generation in original Axis.getOffset()
|
||||
options.title = '';
|
||||
|
||||
if (!axis.axisTitle) {
|
||||
textAlign = axisTitleOptions.textAlign;
|
||||
if (!textAlign) {
|
||||
textAlign = (horiz ? {
|
||||
low: 'left',
|
||||
middle: 'center',
|
||||
high: 'right'
|
||||
} : {
|
||||
low: opposite ? 'right' : 'left',
|
||||
middle: 'center',
|
||||
high: opposite ? 'left' : 'right'
|
||||
})[axisTitleOptions.align];
|
||||
}
|
||||
axis.axisTitle = renderer.text(
|
||||
axisTitleOptions.text,
|
||||
0,
|
||||
0,
|
||||
axisTitleOptions.useHTML
|
||||
)
|
||||
.attr({
|
||||
zIndex: 7,
|
||||
rotation: axisTitleOptions.rotation || 0,
|
||||
align: textAlign
|
||||
})
|
||||
.addClass('highcharts-axis-title')
|
||||
|
||||
// Add to axisParent instead of axisGroup, to ignore the space
|
||||
// it takes
|
||||
.add(axisParent);
|
||||
axis.axisTitle.isNew = true;
|
||||
}
|
||||
|
||||
|
||||
// hide or show the title depending on whether showEmpty is set
|
||||
axis.axisTitle[showAxis ? 'show' : 'hide'](true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add custom date formats
|
||||
*/
|
||||
H.dateFormats = {
|
||||
// Week number
|
||||
W: function(timestamp) {
|
||||
var date = new Date(timestamp),
|
||||
day = date.getUTCDay() === 0 ? 7 : date.getUTCDay(),
|
||||
time = date.getTime(),
|
||||
startOfYear = new Date(date.getUTCFullYear(), 0, 1, -6),
|
||||
dayNumber;
|
||||
date.setDate(date.getUTCDate() + 4 - day);
|
||||
dayNumber = Math.floor((time - startOfYear) / 86400000);
|
||||
return 1 + Math.floor(dayNumber / 7);
|
||||
},
|
||||
// First letter of the day of the week, e.g. 'M' for 'Monday'.
|
||||
E: function(timestamp) {
|
||||
return dateFormat('%a', timestamp, true).charAt(0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevents adding the last tick label if the axis is not a category axis.
|
||||
*
|
||||
* Since numeric labels are normally placed at starts and ends of a range of
|
||||
* value, and this module makes the label point at the value, an "extra" label
|
||||
* would appear.
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*/
|
||||
wrap(Tick.prototype, 'addLabel', function(proceed) {
|
||||
var axis = this.axis,
|
||||
isCategoryAxis = axis.options.categories !== undefined,
|
||||
tickPositions = axis.tickPositions,
|
||||
lastTick = tickPositions[tickPositions.length - 1],
|
||||
isLastTick = this.pos !== lastTick;
|
||||
|
||||
if (!axis.options.grid || isCategoryAxis || isLastTick) {
|
||||
proceed.apply(this);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Center tick labels vertically and horizontally between ticks
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*
|
||||
* @return {object} object - an object containing x and y positions
|
||||
* for the tick
|
||||
*/
|
||||
wrap(Tick.prototype, 'getLabelPosition', function(proceed, x, y, label) {
|
||||
var retVal = proceed.apply(this, Array.prototype.slice.call(arguments, 1)),
|
||||
axis = this.axis,
|
||||
options = axis.options,
|
||||
tickInterval = options.tickInterval || 1,
|
||||
newX,
|
||||
newPos,
|
||||
axisHeight,
|
||||
fontSize,
|
||||
labelMetrics,
|
||||
lblB,
|
||||
lblH,
|
||||
labelCenter;
|
||||
|
||||
// Only center tick labels if axis has option grid: true
|
||||
if (options.grid) {
|
||||
fontSize = options.labels.style.fontSize;
|
||||
labelMetrics = axis.chart.renderer.fontMetrics(fontSize, label);
|
||||
lblB = labelMetrics.b;
|
||||
lblH = labelMetrics.h;
|
||||
|
||||
if (axis.horiz && options.categories === undefined) {
|
||||
// Center x position
|
||||
axisHeight = axis.axisGroup.getBBox().height;
|
||||
newPos = this.pos + tickInterval / 2;
|
||||
retVal.x = axis.translate(newPos) + axis.left;
|
||||
labelCenter = (axisHeight / 2) + (lblH / 2) - Math.abs(lblH - lblB);
|
||||
|
||||
// Center y position
|
||||
if (axis.side === axisSide.top) {
|
||||
retVal.y = y - labelCenter;
|
||||
} else {
|
||||
retVal.y = y + labelCenter;
|
||||
}
|
||||
} else {
|
||||
// Center y position
|
||||
if (options.categories === undefined) {
|
||||
newPos = this.pos + (tickInterval / 2);
|
||||
retVal.y = axis.translate(newPos) + axis.top + (lblB / 2);
|
||||
}
|
||||
|
||||
// Center x position
|
||||
newX = (this.getLabelWidth() / 2) - (axis.maxLabelLength / 2);
|
||||
if (axis.side === axisSide.left) {
|
||||
retVal.x += newX;
|
||||
} else {
|
||||
retVal.x -= newX;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Draw vertical ticks extra long to create cell floors and roofs.
|
||||
* Overrides the tickLength for vertical axes.
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
* @returns {array} retVal -
|
||||
*/
|
||||
wrap(Axis.prototype, 'tickSize', function(proceed) {
|
||||
var axis = this,
|
||||
retVal = proceed.apply(axis, Array.prototype.slice.call(arguments, 1)),
|
||||
labelPadding,
|
||||
distance;
|
||||
|
||||
if (axis.options.grid && !axis.horiz) {
|
||||
labelPadding = (Math.abs(axis.defaultLeftAxisOptions.labels.x) * 2);
|
||||
if (!axis.maxLabelLength) {
|
||||
axis.maxLabelLength = axis.getMaxLabelLength();
|
||||
}
|
||||
distance = axis.maxLabelLength + labelPadding;
|
||||
|
||||
retVal[0] = distance;
|
||||
}
|
||||
return retVal;
|
||||
});
|
||||
|
||||
/**
|
||||
* Disregards space required by axisTitle, by adding axisTitle to axisParent
|
||||
* instead of axisGroup, and disregarding margins and offsets related to
|
||||
* axisTitle.
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*/
|
||||
wrap(Axis.prototype, 'getOffset', function(proceed) {
|
||||
var axis = this,
|
||||
axisOffset = axis.chart.axisOffset,
|
||||
side = axis.side,
|
||||
axisHeight,
|
||||
tickSize,
|
||||
options = axis.options,
|
||||
axisTitleOptions = options.title,
|
||||
addTitle = axisTitleOptions &&
|
||||
axisTitleOptions.text &&
|
||||
axisTitleOptions.enabled !== false;
|
||||
|
||||
if (axis.options.grid && isObject(axis.options.title)) {
|
||||
|
||||
tickSize = axis.tickSize('tick')[0];
|
||||
if (axisOffset[side] && tickSize) {
|
||||
axisHeight = axisOffset[side] + tickSize;
|
||||
}
|
||||
|
||||
if (addTitle) {
|
||||
// Use the custom addTitle() to add it, while preventing making room
|
||||
// for it
|
||||
axis.addTitle();
|
||||
}
|
||||
|
||||
proceed.apply(axis, Array.prototype.slice.call(arguments, 1));
|
||||
|
||||
axisOffset[side] = pick(axisHeight, axisOffset[side]);
|
||||
|
||||
|
||||
// Put axis options back after original Axis.getOffset() has been called
|
||||
options.title = axisTitleOptions;
|
||||
|
||||
} else {
|
||||
proceed.apply(axis, Array.prototype.slice.call(arguments, 1));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Prevents rotation of labels when squished, as rotating them would not
|
||||
* help.
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*/
|
||||
wrap(Axis.prototype, 'renderUnsquish', function(proceed) {
|
||||
if (this.options.grid) {
|
||||
this.labelRotation = 0;
|
||||
this.options.labels.rotation = 0;
|
||||
}
|
||||
proceed.apply(this);
|
||||
});
|
||||
|
||||
/**
|
||||
* Places leftmost tick at the start of the axis, to create a left wall.
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*/
|
||||
wrap(Axis.prototype, 'setOptions', function(proceed, userOptions) {
|
||||
var axis = this;
|
||||
if (userOptions.grid && axis.horiz) {
|
||||
userOptions.startOnTick = true;
|
||||
userOptions.minPadding = 0;
|
||||
userOptions.endOnTick = true;
|
||||
}
|
||||
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
});
|
||||
|
||||
/**
|
||||
* Draw an extra line on the far side of the the axisLine,
|
||||
* creating cell roofs of a grid.
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*/
|
||||
wrap(Axis.prototype, 'render', function(proceed) {
|
||||
var axis = this,
|
||||
options = axis.options,
|
||||
labelPadding,
|
||||
distance,
|
||||
lineWidth,
|
||||
linePath,
|
||||
yStartIndex,
|
||||
yEndIndex,
|
||||
xStartIndex,
|
||||
xEndIndex,
|
||||
renderer = axis.chart.renderer,
|
||||
axisGroupBox;
|
||||
|
||||
if (options.grid) {
|
||||
labelPadding = (Math.abs(axis.defaultLeftAxisOptions.labels.x) * 2);
|
||||
distance = axis.maxLabelLength + labelPadding;
|
||||
lineWidth = options.lineWidth;
|
||||
|
||||
// Remove right wall before rendering
|
||||
if (axis.rightWall) {
|
||||
axis.rightWall.destroy();
|
||||
}
|
||||
|
||||
// Call original Axis.render() to obtain axis.axisLine and
|
||||
// axis.axisGroup
|
||||
proceed.apply(axis);
|
||||
|
||||
axisGroupBox = axis.axisGroup.getBBox();
|
||||
|
||||
// Add right wall on horizontal axes
|
||||
if (axis.horiz) {
|
||||
axis.rightWall = renderer.path([
|
||||
'M',
|
||||
axisGroupBox.x + axis.width + 1, // account for left wall
|
||||
axisGroupBox.y,
|
||||
'L',
|
||||
axisGroupBox.x + axis.width + 1, // account for left wall
|
||||
axisGroupBox.y + axisGroupBox.height
|
||||
])
|
||||
.attr({
|
||||
stroke: options.tickColor || '#ccd6eb',
|
||||
'stroke-width': options.tickWidth || 1,
|
||||
zIndex: 7,
|
||||
class: 'grid-wall'
|
||||
})
|
||||
.add(axis.axisGroup);
|
||||
}
|
||||
|
||||
if (axis.isOuterAxis() && axis.axisLine) {
|
||||
if (axis.horiz) {
|
||||
// -1 to avoid adding distance each time the chart updates
|
||||
distance = axisGroupBox.height - 1;
|
||||
}
|
||||
|
||||
if (lineWidth) {
|
||||
linePath = axis.getLinePath(lineWidth);
|
||||
xStartIndex = linePath.indexOf('M') + 1;
|
||||
xEndIndex = linePath.indexOf('L') + 1;
|
||||
yStartIndex = linePath.indexOf('M') + 2;
|
||||
yEndIndex = linePath.indexOf('L') + 2;
|
||||
|
||||
// Negate distance if top or left axis
|
||||
if (axis.side === axisSide.top || axis.side === axisSide.left) {
|
||||
distance = -distance;
|
||||
}
|
||||
|
||||
// If axis is horizontal, reposition line path vertically
|
||||
if (axis.horiz) {
|
||||
linePath[yStartIndex] = linePath[yStartIndex] + distance;
|
||||
linePath[yEndIndex] = linePath[yEndIndex] + distance;
|
||||
} else {
|
||||
// If axis is vertical, reposition line path horizontally
|
||||
linePath[xStartIndex] = linePath[xStartIndex] + distance;
|
||||
linePath[xEndIndex] = linePath[xEndIndex] + distance;
|
||||
}
|
||||
|
||||
if (!axis.axisLineExtra) {
|
||||
axis.axisLineExtra = renderer.path(linePath)
|
||||
.attr({
|
||||
stroke: options.lineColor,
|
||||
'stroke-width': lineWidth,
|
||||
zIndex: 7
|
||||
})
|
||||
.add(axis.axisGroup);
|
||||
} else {
|
||||
axis.axisLineExtra.animate({
|
||||
d: linePath
|
||||
});
|
||||
}
|
||||
|
||||
// show or hide the line depending on options.showEmpty
|
||||
axis.axisLine[axis.showAxis ? 'show' : 'hide'](true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
proceed.apply(axis);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Wraps chart rendering with the following customizations:
|
||||
* 1. Prohibit timespans of multitudes of a time unit
|
||||
* 2. Draw cell walls on vertical axes
|
||||
*
|
||||
* @param {function} proceed - the original function
|
||||
*/
|
||||
wrap(Chart.prototype, 'render', function(proceed) {
|
||||
// 25 is optimal height for default fontSize (11px)
|
||||
// 25 / 11 ≈ 2.28
|
||||
var fontSizeToCellHeightRatio = 25 / 11,
|
||||
fontMetrics,
|
||||
fontSize;
|
||||
|
||||
each(this.axes, function(axis) {
|
||||
var options = axis.options;
|
||||
if (options.grid) {
|
||||
fontSize = options.labels.style.fontSize;
|
||||
fontMetrics = axis.chart.renderer.fontMetrics(fontSize);
|
||||
|
||||
// Prohibit timespans of multitudes of a time unit,
|
||||
// e.g. two days, three weeks, etc.
|
||||
if (options.type === 'datetime') {
|
||||
options.units = [
|
||||
['millisecond', [1]],
|
||||
['second', [1]],
|
||||
['minute', [1]],
|
||||
['hour', [1]],
|
||||
['day', [1]],
|
||||
['week', [1]],
|
||||
['month', [1]],
|
||||
['year', null]
|
||||
];
|
||||
}
|
||||
|
||||
// Make tick marks taller, creating cell walls of a grid.
|
||||
// Use cellHeight axis option if set
|
||||
if (axis.horiz) {
|
||||
options.tickLength = options.cellHeight ||
|
||||
fontMetrics.h * fontSizeToCellHeightRatio;
|
||||
} else {
|
||||
options.tickWidth = 1;
|
||||
if (!options.lineWidth) {
|
||||
options.lineWidth = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Call original Chart.render()
|
||||
proceed.apply(this);
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
||||
24
static/plugin/chart/highcharts/code/js/modules/heatmap.js
Normal file
24
static/plugin/chart/highcharts/code/js/modules/heatmap.js
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
|
||||
(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 g=b.Axis,t=b.Chart,l=b.color,n,e=b.each,w=b.extend,x=b.isNumber,m=b.Legend,h=b.LegendSymbolMixin,y=b.noop,r=b.merge,v=b.pick,u=b.wrap;n=b.ColorAxis=function(){this.init.apply(this,arguments)};w(n.prototype,g.prototype);w(n.prototype,{defaultColorAxisOptions:{lineWidth:0,minPadding:0,maxPadding:0,gridLineWidth:1,tickPixelInterval:72,startOnTick:!0,endOnTick:!0,offset:0,marker:{animation:{duration:50},
|
||||
width:.01},labels:{overflow:"justify"},minColor:"#e6ebf5",maxColor:"#003399",tickLength:5,showInLegend:!0},init:function(a,c){var d="vertical"!==a.options.legend.layout,f;this.coll="colorAxis";f=r(this.defaultColorAxisOptions,{side:d?2:1,reversed:!d},c,{opposite:!d,showEmpty:!1,title:null});g.prototype.init.call(this,a,f);c.dataClasses&&this.initDataClasses(c);this.initStops(c);this.horiz=d;this.zoomEnabled=!1;this.defaultLegendLength=200},tweenColors:function(a,c,d){var f;c.rgba.length&&a.rgba.length?
|
||||
(a=a.rgba,c=c.rgba,f=1!==c[3]||1!==a[3],a=(f?"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))+(f?","+(c[3]+(a[3]-c[3])*(1-d)):"")+")"):a=c.input||"none";return a},initDataClasses:function(a){var c=this,d,f=0,q=this.chart.options.chart.colorCount,b=this.options,k=a.dataClasses.length;this.dataClasses=d=[];this.legendItems=[];e(a.dataClasses,function(a,e){a=r(a);d.push(a);a.color||("category"===b.dataClassColor?(a.colorIndex=
|
||||
f,f++,f===q&&(f=0)):a.color=c.tweenColors(l(b.minColor),l(b.maxColor),2>k?.5:e/(k-1)))})},initStops:function(a){this.stops=a.stops||[[0,this.options.minColor],[1,this.options.maxColor]];e(this.stops,function(a){a.color=l(a[1])})},setOptions:function(a){g.prototype.setOptions.call(this,a);this.options.crosshair=this.options.marker},setAxisSize:function(){var a=this.legendSymbol,c=this.chart,d=c.options.legend||{},f,q;a?(this.left=d=a.attr("x"),this.top=f=a.attr("y"),this.width=q=a.attr("width"),this.height=
|
||||
a=a.attr("height"),this.right=c.chartWidth-d-q,this.bottom=c.chartHeight-f-a,this.len=this.horiz?q:a,this.pos=this.horiz?d:f):this.len=(this.horiz?d.symbolWidth:d.symbolHeight)||this.defaultLegendLength},toColor:function(a,c){var d=this.stops,f,q,b=this.dataClasses,k,e;if(b)for(e=b.length;e--;){if(k=b[e],f=k.from,d=k.to,(void 0===f||a>=f)&&(void 0===d||a<=d)){q=k.color;c&&(c.dataClass=e,c.colorIndex=k.colorIndex);break}}else{this.isLog&&(a=this.val2lin(a));a=1-(this.max-a)/(this.max-this.min||1);
|
||||
for(e=d.length;e--&&!(a>d[e][0]););f=d[e]||d[e+1];d=d[e+1]||f;a=1-(d[0]-a)/(d[0]-f[0]||1);q=this.tweenColors(f.color,d.color,a)}return q},getOffset:function(){var a=this.legendGroup,c=this.chart.axisOffset[this.side];a&&(this.axisParent=a,g.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,f=a.options,b=this.horiz,e=v(f.symbolWidth,b?this.defaultLegendLength:12),k=v(f.symbolHeight,b?12:this.defaultLegendLength),h=v(f.labelPadding,b?16:30),f=v(f.itemDistance,10);this.setLegendColor();c.legendSymbol=this.chart.renderer.rect(0,a.baseline-11,e,k).attr({zIndex:1}).add(c.legendGroup);this.legendItemWidth=e+d+(b?f:h);this.legendItemHeight=k+d+(b?
|
||||
h:0)},setState:y,visible:!0,setVisible:y,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,b=c&&c.plotY,e,h=this.pos,k=this.len;c&&(e=this.toPixels(c[c.series.colorKey]),e<h?e=h-2:e>h+k&&(e=h+k+2),c.plotX=e,c.plotY=this.len-e,g.prototype.drawCrosshair.call(this,a,c),c.plotX=d,c.plotY=b,this.cross&&this.cross.addClass("highcharts-coloraxis-marker").add(this.legendGroup))},getPlotLinePath:function(a,
|
||||
c,d,b,e){return x(e)?this.horiz?["M",e-4,this.top-6,"L",e+4,this.top-6,e,this.top,"Z"]:["M",this.left,e,"L",this.left-6,e+6,this.left-6,e-6,"Z"]:g.prototype.getPlotLinePath.call(this,a,c,d,b)},update:function(a,c){var d=this.chart,b=d.legend;e(this.series,function(a){a.isDirtyData=!0});a.dataClasses&&b.allItems&&(e(b.allItems,function(a){a.isDataClass&&a.legendGroup.destroy()}),d.isDirtyLegend=!0);d.options[this.coll]=r(this.userOptions,a);g.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,f=c.options.legend,n=f.valueDecimals,u=f.valueSuffix||"",k;d.length||e(this.dataClasses,function(f,m){var g=!0,r=f.from,l=f.to;k="";void 0===r?k="\x3c ":void 0===l&&(k="\x3e ");void 0!==r&&(k+=b.numberFormat(r,n)+u);void 0!==r&&void 0!==l&&(k+=" - ");void 0!==l&&(k+=b.numberFormat(l,n)+u);d.push(w({chart:c,name:k,options:{},drawLegendSymbol:h.drawRectangle,visible:!0,setState:y,isDataClass:!0,
|
||||
setVisible:function(){g=this.visible=!g;e(a.series,function(a){e(a.points,function(a){a.dataClass===m&&a.setVisible(g)})});c.legend.colorizeItem(this,g)}},f))});return d},name:""});e(["fill","stroke"],function(a){b.Fx.prototype[a+"Setter"]=function(){this.elem.attr(a,n.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 n(this,c)});u(m.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)),e(d.series,function(a){a.options.showInLegend=!1}));return c.concat(a.call(this))});u(m.prototype,"colorizeItem",function(a,c,d){a.call(this,c,d);d&&c.legendColor&&c.legendSymbol.attr({fill:c.legendColor})})})(p);(function(b){var g=b.defined,t=b.each,l=b.noop;b.colorPointMixin={isValid:function(){return null!==this.value},setVisible:function(b){var e=this,g=b?"show":"hide";t(["graphic",
|
||||
"dataLabel"],function(b){if(e[b])e[b][g]()})}};b.colorSeriesMixin={pointArrayMap:["value"],axisTypes:["xAxis","yAxis","colorAxis"],optionalAxis:"colorAxis",trackerGroups:["group","markerGroup","dataLabelsGroup"],getSymbol:l,parallelArrays:["x","y","value"],colorKey:"value",translateColors:function(){var b=this,e=this.options.nullColor,g=this.colorAxis,l=this.colorKey;t(this.data,function(m){var h=m[l];if(h=m.options.color||(m.isNull?e:g&&void 0!==h?g.toColor(h,m):m.color||b.color))m.color=h})},colorAttribs:function(b){var e=
|
||||
{};g(b.color)&&(e[this.colorProp||"fill"]=b.color);return e}}})(p);(function(b){var g=b.colorPointMixin,t=b.each,l=b.merge,n=b.noop,e=b.pick,p=b.Series,x=b.seriesType,m=b.seriesTypes;x("heatmap","scatter",{animation:!1,borderWidth:0,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}\x3cbr/\x3e"},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;m.scatter.prototype.init.apply(this,arguments);b=this.options;b.pointRange=e(b.pointRange,b.colsize||1);this.yAxis.axisPointRange=b.rowsize||1},translate:function(){var b=this.options,e=this.xAxis,g=this.yAxis,l=function(b,a,c){return Math.min(Math.max(a,b),c)};this.generatePoints();t(this.points,function(h){var a=(b.colsize||1)/2,c=(b.rowsize||
|
||||
1)/2,d=l(Math.round(e.len-e.translate(h.x-a,0,1,0,1)),-e.len,2*e.len),a=l(Math.round(e.len-e.translate(h.x+a,0,1,0,1)),-e.len,2*e.len),f=l(Math.round(g.translate(h.y-c,0,1,0,1)),-g.len,2*g.len),c=l(Math.round(g.translate(h.y+c,0,1,0,1)),-g.len,2*g.len);h.plotX=h.clientX=(d+a)/2;h.plotY=(f+c)/2;h.shapeType="rect";h.shapeArgs={x:Math.min(d,a),y:Math.min(f,c),width:Math.abs(a-d),height:Math.abs(c-f)}});this.translateColors()},drawPoints:function(){m.column.prototype.drawPoints.call(this);t(this.points,
|
||||
function(b){b.graphic.attr(this.colorAttribs(b,b.state))},this)},animate:n,getBox:n,drawLegendSymbol:b.LegendSymbolMixin.drawRectangle,alignDataLabel:m.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)}}),g)})(p)});
|
||||
769
static/plugin/chart/highcharts/code/js/modules/heatmap.src.js
Normal file
769
static/plugin/chart/highcharts/code/js/modules/heatmap.src.js
Normal file
@ -0,0 +1,769 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
*
|
||||
* (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
|
||||
|
||||
},
|
||||
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') {
|
||||
|
||||
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);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
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 = {
|
||||
/**
|
||||
* Color points have a value option that determines whether or not it is a null point
|
||||
*/
|
||||
isValid: function() {
|
||||
return this.value !== null;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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',
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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 ||
|
||||
(point.isNull ? 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,
|
||||
|
||||
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));
|
||||
}));
|
||||
@ -0,0 +1,12 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
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 f(){this.hasData()?this.hideNoData():this.showNoData()}var g=c.seriesTypes,e=c.Chart.prototype,h=c.getOptions(),k=c.extend,l=c.each;k(h.lang,{noData:"No data to display"});h.noData={position:{x:0,y:0,align:"center",verticalAlign:"middle"}};l(["pie","gauge","waterfall","bubble","treemap"],function(a){g[a]&&(g[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.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",f);c.addEvent(a,"redraw",f)})})(d)});
|
||||
@ -0,0 +1,150 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
* 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
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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.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));
|
||||
}));
|
||||
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
Client side exporting module
|
||||
|
||||
(c) 2015 Torstein Honsi / Oystein Moseng
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(g){"object"===typeof module&&module.exports?module.exports=g:g(Highcharts)})(function(g){(function(b){function v(d,b){var c=r.getElementsByTagName("head")[0],a=r.createElement("script");a.type="text/javascript";a.src=d;a.onload=b;a.onerror=function(){console.error("Error loading script",d)};c.appendChild(a)}var g=b.merge,e=b.win,t=e.navigator,r=e.document,z=b.each,A=e.URL||e.webkitURL||e,B=/Edge\/|Trident\/|MSIE /.test(t.userAgent),C=B?150:0;b.CanVGRenderer={};b.downloadURL=function(d,b){var c=
|
||||
r.createElement("a"),a;if(t.msSaveOrOpenBlob)t.msSaveOrOpenBlob(d,b);else if(void 0!==c.download)c.href=d,c.download=b,c.target="_blank",r.body.appendChild(c),c.click(),r.body.removeChild(c);else try{if(a=e.open(d,"chart"),void 0===a||null===a)throw"Failed to open window";}catch(u){e.location.href=d}};b.svgToDataUrl=function(d){var b=-1<t.userAgent.indexOf("WebKit")&&0>t.userAgent.indexOf("Chrome");try{if(!b&&0>t.userAgent.toLowerCase().indexOf("firefox"))return A.createObjectURL(new e.Blob([d],{type:"image/svg+xml;charset-utf-16"}))}catch(c){}return"data:image/svg+xml;charset\x3dUTF-8,"+
|
||||
encodeURIComponent(d)};b.imageToDataUrl=function(b,f,c,a,u,l,k,m,p){var d=new e.Image,h,q=function(){setTimeout(function(){var e=r.createElement("canvas"),n=e.getContext&&e.getContext("2d"),w;try{if(n){e.height=d.height*a;e.width=d.width*a;n.drawImage(d,0,0,e.width,e.height);try{w=e.toDataURL(f),u(w,f,c,a)}catch(D){h(b,f,c,a)}}else k(b,f,c,a)}finally{p&&p(b,f,c,a)}},C)},n=function(){m(b,f,c,a);p&&p(b,f,c,a)};h=function(){d=new e.Image;h=l;d.crossOrigin="Anonymous";d.onload=q;d.onerror=n;d.src=b};
|
||||
d.onload=q;d.onerror=n;d.src=b};b.downloadSVGLocal=function(d,f,c,a){function u(b,a){a=new e.jsPDF("l","pt",[b.width.baseVal.value+2*a,b.height.baseVal.value+2*a]);e.svgElementToPdf(b,a,{removeInvalid:!0});return a.output("datauristring")}function l(){q.innerHTML=d;var c=q.getElementsByTagName("text"),e=q.getElementsByTagName("svg")[0].style;z(c,function(a){z(["font-family","font-size"],function(b){!a.style[b]&&e[b]&&(a.style[b]=e[b])});a.style["font-family"]=a.style["font-family"]&&a.style["font-family"].split(" ").splice(-1)});
|
||||
c=u(q.firstChild,0);b.downloadURL(c,g);a&&a()}var k,m,p=!0,x,h=f.libURL||b.getOptions().exporting.libURL,q=r.createElement("div"),n=f.type||"image/png",g=(f.filename||"chart")+"."+("image/svg+xml"===n?"svg":n.split("/")[1]),y=f.scale||1,h="/"!==h.slice(-1)?h+"/":h;if("image/svg+xml"===n)try{t.msSaveOrOpenBlob?(m=new MSBlobBuilder,m.append(d),k=m.getBlob("image/svg+xml")):k=b.svgToDataUrl(d),b.downloadURL(k,g),a&&a()}catch(w){c()}else"application/pdf"===n?e.jsPDF&&e.svgElementToPdf?l():(p=!0,v(h+"jspdf.js",
|
||||
function(){v(h+"rgbcolor.js",function(){v(h+"svg2pdf.js",function(){l()})})})):(k=b.svgToDataUrl(d),x=function(){try{A.revokeObjectURL(k)}catch(w){}},b.imageToDataUrl(k,n,{},y,function(d){try{b.downloadURL(d,g),a&&a()}catch(D){c()}},function(){var f=r.createElement("canvas"),u=f.getContext("2d"),l=d.match(/^<svg[^>]*width\s*=\s*\"?(\d+)\"?[^>]*>/)[1]*y,k=d.match(/^<svg[^>]*height\s*=\s*\"?(\d+)\"?[^>]*>/)[1]*y,m=function(){u.drawSvg(d,0,0,l,k);try{b.downloadURL(t.msSaveOrOpenBlob?f.msToBlob():f.toDataURL(n),
|
||||
g),a&&a()}catch(E){c()}finally{x()}};f.width=l;f.height=k;e.canvg?m():(p=!0,v(h+"rgbcolor.js",function(){v(h+"canvg.js",function(){m()})}))},c,c,function(){p&&x()}))};b.Chart.prototype.getSVGForLocalExport=function(d,e,c,a){var f=this,l,k=0,m,p,g,h,q=function(b,d,c){++k;c.imageElement.setAttributeNS("http://www.w3.org/1999/xlink","href",b);k===l.length&&a(f.sanitizeSVG(m.innerHTML))};b.wrap(b.Chart.prototype,"getChartHTML",function(a){m=this.container.cloneNode(!0);return a.apply(this,Array.prototype.slice.call(arguments,
|
||||
1))});f.getSVGForExport(d,e);l=m.getElementsByTagName("image");try{if(l.length)for(g=0,h=l.length;g<h;++g)p=l[g],b.imageToDataUrl(p.getAttributeNS("http://www.w3.org/1999/xlink","href"),"image/png",{imageElement:p},d.scale,q,c,c,c);else a(f.sanitizeSVG(m.innerHTML))}catch(n){c()}};b.Chart.prototype.exportChartLocal=function(d,e){var c=this,a=b.merge(c.options.exporting,d),f=function(){if(!1===a.fallbackToExportServer)if(a.error)a.error();else throw"Fallback to export server disabled";else c.exportChart(a)};
|
||||
(B&&"image/svg+xml"!==a.imageType||"application/pdf"!==a.imageType)&&c.container.getElementsByTagName("image").length?f():c.getSVGForLocalExport(a,e,f,function(c){b.downloadSVGLocal(c,a,f)})};g(!0,b.getOptions().exporting,{libURL:"http://code.highcharts.com/5.0.2/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"})}},{textKey:"downloadPDF",onclick:function(){this.exportChartLocal({type:"application/pdf"})}}]}}})})(g)});
|
||||
@ -0,0 +1,457 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
* 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,
|
||||
each = Highcharts.each,
|
||||
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;
|
||||
script.onerror = function() {
|
||||
console.error('Error loading script', scriptLocation); // eslint-disable-line no-console
|
||||
};
|
||||
|
||||
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
|
||||
*
|
||||
* options object:
|
||||
* filename: Name of resulting downloaded file without extension
|
||||
* type: File type of resulting download
|
||||
* scale: Scaling factor of downloaded image compared to source
|
||||
* libURL: URL pointing to location of dependency scripts to download on demand
|
||||
*/
|
||||
Highcharts.downloadSVGLocal = function(svg, options, failCallback, successCallback) {
|
||||
var svgurl,
|
||||
blob,
|
||||
objectURLRevoke = true,
|
||||
finallyHandler,
|
||||
libURL = options.libURL || Highcharts.getOptions().exporting.libURL,
|
||||
dummySVGContainer = doc.createElement('div'),
|
||||
imageType = options.type || 'image/png',
|
||||
filename = (options.filename || 'chart') + '.' + (imageType === 'image/svg+xml' ? 'svg' : imageType.split('/')[1]),
|
||||
scale = options.scale || 1;
|
||||
|
||||
libURL = libURL.slice(-1) !== '/' ? libURL + '/' : libURL; // Allow libURL to end with or without fordward slash
|
||||
|
||||
function svgToPdf(svgElement, margin) {
|
||||
var width = svgElement.width.baseVal.value + 2 * margin,
|
||||
height = svgElement.height.baseVal.value + 2 * margin,
|
||||
pdf = new win.jsPDF('l', 'pt', [width, height]); // eslint-disable-line new-cap
|
||||
win.svgElementToPdf(svgElement, pdf, {
|
||||
removeInvalid: true
|
||||
});
|
||||
return pdf.output('datauristring');
|
||||
}
|
||||
|
||||
function downloadPDF() {
|
||||
dummySVGContainer.innerHTML = svg;
|
||||
var textElements = dummySVGContainer.getElementsByTagName('text'),
|
||||
svgElementStyle = dummySVGContainer.getElementsByTagName('svg')[0].style;
|
||||
// Workaround for the text styling. Making sure it does pick up the root element
|
||||
each(textElements, function(el) {
|
||||
each(['font-family', 'font-size'], function(property) {
|
||||
if (!el.style[property] && svgElementStyle[property]) {
|
||||
el.style[property] = svgElementStyle[property];
|
||||
}
|
||||
});
|
||||
el.style['font-family'] = el.style['font-family'] && el.style['font-family'].split(' ').splice(-1);
|
||||
});
|
||||
var svgData = svgToPdf(dummySVGContainer.firstChild, 0);
|
||||
Highcharts.downloadURL(svgData, filename);
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
}
|
||||
|
||||
// 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') {
|
||||
if (win.jsPDF && win.svgElementToPdf) {
|
||||
downloadPDF();
|
||||
} else {
|
||||
// Must load pdf libraries 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.
|
||||
getScript(libURL + 'jspdf.js', function() {
|
||||
getScript(libURL + 'rgbcolor.js', function() {
|
||||
getScript(libURL + 'svg2pdf.js', function() {
|
||||
downloadPDF();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
} 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.
|
||||
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),
|
||||
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) {
|
||||
Highcharts.downloadSVGLocal(svg, options, fallbackToExportServer);
|
||||
};
|
||||
|
||||
// If we have embedded images and are exporting to JPEG/PNG, Microsoft browsers won't handle it, so fall back
|
||||
if ((isMSBrowser && options.imageType !== 'image/svg+xml' || options.imageType !== 'application/pdf') && 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/5.0.2/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));
|
||||
}));
|
||||
@ -0,0 +1,10 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
|
||||
(c) 2009-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(a){"object"===typeof module&&module.exports?module.exports=a:a(Highcharts)})(function(a){(function(a){var h=a.Chart,k=a.each,q=a.pick,r=a.addEvent;h.prototype.callbacks.push(function(f){function a(){var a=[];k(f.series,function(b){var c=b.options.dataLabels,d=b.dataLabelCollections||["dataLabel"];(c.enabled||b._hasPointLabels)&&!c.allowOverlap&&b.visible&&k(d,function(c){k(b.points,function(b){b[c]&&(b[c].labelrank=q(b.labelrank,b.shapeArgs&&b.shapeArgs.height),a.push(b[c]))})})});f.hideOverlappingLabels(a)}
|
||||
a();r(f,"redraw",a)});h.prototype.hideOverlappingLabels=function(a){var f=a.length,e,b,c,d,l,m,n,p,g,h=function(a,b,c,d,e,f,g,h){return!(e>a+c||e+g<a||f>b+d||f+h<b)};for(b=0;b<f;b++)if(e=a[b])e.oldOpacity=e.opacity,e.newOpacity=1;a.sort(function(a,b){return(b.labelrank||0)-(a.labelrank||0)});for(b=0;b<f;b++)for(c=a[b],e=b+1;e<f;++e)if(d=a[e],c&&d&&c.placed&&d.placed&&0!==c.newOpacity&&0!==d.newOpacity&&(l=c.alignAttr,m=d.alignAttr,n=c.parentGroup,p=d.parentGroup,g=2*(c.box?0:c.padding),l=h(l.x+n.translateX,
|
||||
l.y+n.translateY,c.width-g,c.height-g,m.x+p.translateX,m.y+p.translateY,d.width-g,d.height-g)))(c.labelrank<d.labelrank?c:d).newOpacity=0;k(a,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)})}})(a)});
|
||||
@ -0,0 +1,164 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
*
|
||||
* (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));
|
||||
}));
|
||||
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
|
||||
(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)});
|
||||
@ -0,0 +1,544 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
*
|
||||
* (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));
|
||||
}));
|
||||
@ -0,0 +1,14 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
Solid angular gauge module
|
||||
|
||||
(c) 2010-2016 Torstein Honsi
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(k){"object"===typeof module&&module.exports?module.exports=k:k(Highcharts)})(function(k){(function(d){var k=d.pInt,t=d.pick,l=d.each,u=d.isNumber,m;m={initDataClasses:function(a){var c=this,e=this.chart,h,r=0,f=this.options;this.dataClasses=h=[];l(a.dataClasses,function(g,b){g=d.merge(g);h.push(g);g.color||("category"===f.dataClassColor?(b=e.options.colors,g.color=b[r++],r===b.length&&(r=0)):g.color=c.tweenColors(d.color(f.minColor),d.color(f.maxColor),b/(a.dataClasses.length-1)))})},initStops:function(a){this.stops=
|
||||
a.stops||[[0,this.options.minColor],[1,this.options.maxColor]];l(this.stops,function(a){a.color=d.color(a[1])})},toColor:function(a,c){var e=this.stops,h,d,f=this.dataClasses,g,b;if(f)for(b=f.length;b--;){if(g=f[b],h=g.from,e=g.to,(void 0===h||a>=h)&&(void 0===e||a<=e)){d=g.color;c&&(c.dataClass=b);break}}else{this.isLog&&(a=this.val2lin(a));a=1-(this.max-a)/(this.max-this.min);for(b=e.length;b--&&!(a>e[b][0]););h=e[b]||e[b+1];e=e[b+1]||h;a=1-(e[0]-a)/(e[0]-h[0]||1);d=this.tweenColors(h.color,e.color,
|
||||
a)}return d},tweenColors:function(a,c,e){var d;c.rgba.length&&a.rgba.length?(a=a.rgba,c=c.rgba,d=1!==c[3]||1!==a[3],a=(d?"rgba(":"rgb(")+Math.round(c[0]+(a[0]-c[0])*(1-e))+","+Math.round(c[1]+(a[1]-c[1])*(1-e))+","+Math.round(c[2]+(a[2]-c[2])*(1-e))+(d?","+(c[3]+(a[3]-c[3])*(1-e)):"")+")"):a=c.input||"none";return a}};l(["fill","stroke"],function(a){d.Fx.prototype[a+"Setter"]=function(){this.elem.attr(a,m.tweenColors(d.color(this.start),d.color(this.end),this.pos))}});d.seriesType("solidgauge","gauge",
|
||||
{colorByPoint:!0},{bindAxes:function(){var a;d.seriesTypes.gauge.prototype.bindAxes.call(this);a=this.yAxis;d.extend(a,m);a.options.dataClasses&&a.initDataClasses(a.options);a.initStops(a.options)},drawPoints:function(){var a=this,c=a.yAxis,e=c.center,d=a.options,r=a.chart.renderer,f=d.overshoot,g=u(f)?f/180*Math.PI:0;l(a.points,function(b){var h=b.graphic,f=c.startAngleRad+c.translate(b.y,null,null,null,!0),l=k(t(b.options.radius,d.radius,100))*e[2]/200,n=k(t(b.options.innerRadius,d.innerRadius,
|
||||
60))*e[2]/200,p=c.toColor(b.y,b),q=Math.min(c.startAngleRad,c.endAngleRad),m=Math.max(c.startAngleRad,c.endAngleRad);"none"===p&&(p=b.color||a.color||"none");"none"!==p&&(b.color=p);f=Math.max(q-g,Math.min(m+g,f));!1===d.wrap&&(f=Math.max(q,Math.min(m,f)));q=Math.min(f,c.startAngleRad);f=Math.max(f,c.startAngleRad);f-q>2*Math.PI&&(f=q+2*Math.PI);b.shapeArgs=n={x:e[0],y:e[1],r:l,innerR:n,start:q,end:f,fill:p};b.startR=l;h?(b=n.d,h.animate(n),b&&(n.d=b)):b.graphic=r.arc(n).addClass("highcharts-point").attr({fill:p,
|
||||
"sweep-flag":0}).add(a.group)})},animate:function(a){a||(this.startAngleRad=this.yAxis.startAngleRad,d.seriesTypes.pie.prototype.animate.call(this,a))}})})(k)});
|
||||
@ -0,0 +1,277 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
* 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);
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}));
|
||||
29
static/plugin/chart/highcharts/code/js/modules/treemap.js
Normal file
29
static/plugin/chart/highcharts/code/js/modules/treemap.js
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
|
||||
(c) 2014 Highsoft AS
|
||||
Authors: Jon Arild Nygard / Oystein Moseng
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(p){"object"===typeof module&&module.exports?module.exports=p:p(Highcharts)})(function(p){(function(g){var p=g.seriesType,t=g.seriesTypes,D=g.map,w=g.merge,z=g.extend,A=g.noop,m=g.each,y=g.grep,E=g.isNumber,k=g.pick,u=g.Series,F=g.stableSort,G=function(a,b,c){var e;c=c||this;for(e in a)a.hasOwnProperty(e)&&b.call(c,a[e],e,a)},B=function(a,b,c,e){e=e||this;a=a||[];m(a,function(d,f){c=b.call(e,c,d,f,a)});return c},x=function(a,b,c){c=c||this;a=b.call(c,a);!1!==a&&x(a,b,c)};p("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:"\x3cb\x3e{point.name}\x3c/b\x3e: {point.value}\x3c/b\x3e\x3cbr/\x3e"},layoutAlgorithm:"sliceAndDice",layoutStartingDirection:"vertical",alternateStartingDirection:!1,levelIsConstant:!0,drillUpButton:{position:{align:"right",x:-10,y:10}}},{pointArrayMap:["value"],axisTypes:t.heatmap?["xAxis","yAxis","colorAxis"]:
|
||||
["xAxis","yAxis"],optionalAxis:"colorAxis",getSymbol:A,parallelArrays:["x","y","value","colorValue"],colorKey:"colorValue",translateColors:t.heatmap&&t.heatmap.prototype.translateColors,trackerGroups:["group","dataLabelsGroup"],getListOfParents:function(a,b){a=B(a,function(a,b,d){b=k(b.parent,"");void 0===a[b]&&(a[b]=[]);a[b].push(d);return a},{});G(a,function(a,e,d){""!==e&&-1===g.inArray(e,b)&&(m(a,function(a){d[""].push(a)}),delete d[e])});return a},getTree:function(){var a,b=this;a=D(this.data,
|
||||
function(a){return a.id});a=b.getListOfParents(this.data,a);b.nodeMap=[];a=b.buildNode("",-1,0,a,null);x(this.nodeMap[this.rootNode],function(a){var c=!1,d=a.parent;a.visible=!0;if(d||""===d)c=b.nodeMap[d];return c});x(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,e,d){var f=this,h=[],C=f.points[b],v;m(e[a]||[],function(b){v=f.buildNode(f.points[b].id,b,c+1,e,a);h.push(v)});b={id:a,i:b,children:h,level:c,parent:d,visible:!1};f.nodeMap[b.id]=b;C&&(C.node=b);return b},setTreeValues:function(a){var b=this,c=b.options,e=0,d=[],f,h=b.points[a.i];m(a.children,function(a){a=b.setTreeValues(a);d.push(a);a.ignore?x(a.children,function(a){var b=!1;m(a,function(a){z(a,{ignore:!0,isLeaf:!1,visible:!1});a.children.length&&(b=(b||[]).concat(a.children))});return b}):
|
||||
e+=a.val});F(d,function(a,b){return a.sortIndex-b.sortIndex});f=k(h&&h.options.value,e);h&&(h.value=f);z(a,{children:d,childrenTotal:e,ignore:!(k(h&&h.visible,!0)&&0<f),isLeaf:a.visible&&!e,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,e=c.options,d=this.levelMap[a.levelDynamic+1],f=k(c[d&&d.layoutAlgorithm]&&d.layoutAlgorithm,e.layoutAlgorithm),h=e.alternateStartingDirection,
|
||||
g=[];a=y(a.children,function(a){return!a.ignore});d&&d.layoutStartingDirection&&(b.direction="vertical"===d.layoutStartingDirection?0:1);g=c[f](b,a);m(a,function(a,d){d=g[d];a.values=w(d,{val:a.childrenTotal,direction:h?1-b.direction:b.direction});a.pointValues=w(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 e=c.node,d=e.pointValues,f,h;d&&e.visible?(e=
|
||||
Math.round(a.translate(d.x,0,0,0,1))-.5,f=Math.round(a.translate(d.x+d.width,0,0,0,1))-.5,h=Math.round(b.translate(d.y,0,0,0,1))-.5,d=Math.round(b.translate(d.y+d.height,0,0,0,1))-.5,c.shapeType="rect",c.shapeArgs={x:Math.min(e,f),y:Math.min(h,d),width:Math.abs(f-e),height:Math.abs(d-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 e=this,d,f;a&&(d=e.points[a.i],f=e.levelMap[a.levelDynamic],
|
||||
b=k(d&&d.options.color,f&&f.color,b),c=k(d&&d.options.colorIndex,f&&f.colorIndex,c),d&&(d.color=b,d.colorIndex=c),a.children.length&&m(a.children,function(a){e.setColorRecursive(a,b,c)}))},algorithmGroup:function(a,b,c,e){this.height=a;this.width=b;this.plot=e;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,b){return Math.max(a/b,b/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,e){var d,f,h,g,v=c.lW,n=c.lH,l=c.plot,k,q=0,r=c.elArr.length-1;b?(v=c.nW,n=c.nH):k=c.elArr[c.elArr.length-1];m(c.elArr,function(a){if(b||q<r)0===c.direction?(d=l.x,f=l.y,h=v,g=a/h):(d=l.x,f=l.y,g=n,h=a/g),e.push({x:d,y:f,width:h,height:g}),0===c.direction?l.y+=g:l.x+=h;q+=1});c.reset();0===c.direction?c.width-=v:c.height-=n;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(k)},algorithmLowAspectRatio:function(a,b,c){var e=[],d=this,f,h={x:b.x,y:b.y,parent:b},g=0,k=c.length-1,n=new this.algorithmGroup(b.height,b.width,b.direction,h);m(c,function(c){f=c.val/b.val*b.height*b.width;n.addElement(f);n.lP.nR>n.lP.lR&&d.algorithmCalcPoints(a,!1,n,e,h);g===k&&d.algorithmCalcPoints(a,!0,n,e,h);g+=1});return e},algorithmFill:function(a,b,c){var e=[],d,f=b.direction,h=b.x,g=b.y,k=b.width,n=b.height,l,p,q,r;m(c,function(c){d=c.val/b.val*b.height*b.width;l=h;p=g;
|
||||
0===f?(r=n,q=d/r,k-=q,h+=q):(q=k,r=d/q,n-=r,g+=r);e.push({x:l,y:p,width:q,height:r});a&&(f=1-f)});return e},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=w(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=y(a.points,function(a){return a.node.visible}),c,e;m(b,function(b){e=a.levelMap[b.node.levelDynamic];c={style:{}};b.node.isLeaf||(c.enabled=!1);e&&e.dataLabels&&(c=w(c,e.dataLabels),a._hasPointLabels=!0);b.shapeArgs&&(c.style.width=b.shapeArgs.width,b.dataLabel&&b.dataLabel.css({width:b.shapeArgs.width+"px"}));
|
||||
b.dlOptions=w(c,b.options.dataLabels)});u.prototype.drawDataLabels.call(this)},alignDataLabel:function(a){t.column.prototype.alignDataLabel.apply(this,arguments);a.dataLabel&&a.dataLabel.attr({zIndex:a.node.zIndex+1})},drawPoints:function(){var a=this,b=y(a.points,function(a){return a.node.visible});m(b,function(b){var c="levelGroup-"+b.node.levelDynamic;a[c]||(a[c]=a.chart.renderer.g(c).attr({zIndex:1E3-b.node.levelDynamic}).add(a.group));b.group=a[c]});t.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,e;c&&(e=a.nodeMap[a.rootNode].name||a.rootNode,b.setState(""),a.drillToNode(c),a.showDrillUpButton(e))})},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||
|
||||
"\x3c Back";var c=b.options.drillUpButton,e,d;c.text&&(a=c.text);this.drillUpButton?this.drillUpButton.attr({text:a}).align():(d=(e=c.theme)&&e.states,this.drillUpButton=this.chart.renderer.button(a,null,null,function(){b.drillUp()},e,d&&d.hover,d&&d.select).attr({align:c.position.align,zIndex:7}).add().align(c.position,!1,c.relativeTo||"plotBox"))},buildKDTree:A,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},isValid:function(){return E(this.value)},setState:function(a){g.Point.prototype.setState.call(this,a);this.graphic.attr({zIndex:"hover"===a?1:0})},setVisible:t.pie.prototype.pointClass.prototype.setVisible})})(p)});
|
||||
881
static/plugin/chart/highcharts/code/js/modules/treemap.src.js
Normal file
881
static/plugin/chart/highcharts/code/js/modules/treemap.src.js
Normal file
@ -0,0 +1,881 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
*
|
||||
* (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,
|
||||
isNumber = H.isNumber,
|
||||
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
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// 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
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
},
|
||||
isValid: function() {
|
||||
return isNumber(this.value);
|
||||
},
|
||||
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));
|
||||
}));
|
||||
@ -0,0 +1,15 @@
|
||||
/*
|
||||
Highcharts JS v5.0.2 (2016-10-26)
|
||||
X-range series
|
||||
|
||||
(c) 2010-2016 Torstein Honsi, Lars A. V. Cabrera
|
||||
|
||||
--- WORK IN PROGRESS ---
|
||||
|
||||
License: www.highcharts.com/license
|
||||
*/
|
||||
(function(h){"object"===typeof module&&module.exports?module.exports=h:h(Highcharts)})(function(h){(function(c){var h=c.getOptions().plotOptions,m=c.seriesTypes.column,k=c.each,p=c.extendClass,q=c.isNumber,v=c.isObject,n=c.merge,r=c.pick,t=c.seriesTypes,w=c.stop,x=c.wrap,y=c.Axis,u=c.Point,z=c.Series;h.xrange=n(h.column,{tooltip:{pointFormat:'\x3cspan style\x3d"color:{point.color}"\x3e\u25cf\x3c/span\x3e {series.name}: \x3cb\x3e{point.yCategory}\x3c/b\x3e\x3cbr/\x3e'}});t.xrange=p(m,{pointClass:p(u,
|
||||
{getLabelConfig:function(){var g=u.prototype.getLabelConfig.call(this);g.x2=this.x2;g.yCategory=this.yCategory=this.series.yAxis.categories&&this.series.yAxis.categories[this.y];return g}}),type:"xrange",forceDL:!0,parallelArrays:["x","x2","y"],requireSorting:!1,animate:t.line.prototype.animate,getColumnMetrics:function(){function g(){k(f.series,function(b){var a=b.xAxis;b.xAxis=b.yAxis;b.yAxis=a})}var e,f=this.chart;g();this.yAxis.closestPointRange=1;e=m.prototype.getColumnMetrics.call(this);g();
|
||||
return e},cropData:function(g,e,f,b){e=z.prototype.cropData.call(this,this.x2Data,e,f,b);e.xData=g.slice(e.start,e.end);return e},translate:function(){m.prototype.translate.apply(this,arguments);var g=this.xAxis,e=this.columnMetrics,f=this.options.minPointLength||0;k(this.points,function(b){var a=b.plotX,d=r(b.x2,b.x+(b.len||0)),d=g.toPixels(d,!0),c=d-a,l;f&&(l=f-c,0>l&&(l=0),a-=l/2,d+=l/2);a=Math.max(a,-10);d=Math.min(Math.max(d,-10),g.len+10);b.shapeArgs={x:a,y:b.plotY+e.offset,width:d-a,height:e.width};
|
||||
b.tooltipPos[0]+=c/2;b.tooltipPos[1]-=e.width/2;if(d=b.partialFill)v(d)&&(d=d.amount),q(d)||(d=0),a=b.shapeArgs,b.partShapeArgs={x:a.x,y:a.y+1,width:a.width*d,height:a.height-2}})},drawPoints:function(){var c=this,e=this.chart,f=e.renderer,b=e.pointCount<(c.options.animationLimit||250)?"animate":"attr";k(c.points,function(a){var d=a.graphic,e=a.shapeType,g=a.shapeArgs,h=a.partShapeArgs;if(q(a.plotY)&&null!==a.y)if(d){if(w(d),a.graphicOriginal[b](n(g)),h)a.graphicOverlay[b](n(h))}else a.graphic=d=
|
||||
f.g("point").attr({"class":a.getClassName()}).add(a.group||c.group),a.graphicOriginal=f[e](g).addClass("highcharts-partfill-original").add(d),h&&(a.graphicOverlay=f[e](h).addClass("highcharts-partfill-overlay").add(d));else d&&(a.graphic=d.destroy())})}});x(y.prototype,"getSeriesExtremes",function(c){var e=this.series,f,b;c.call(this);this.isXAxis&&"xrange"===e.type&&(f=r(this.dataMax,Number.MIN_VALUE),k(this.series,function(a){k(a.x2Data||[],function(a){a>f&&(f=a,b=!0)})}),b&&(this.dataMax=f))})})(h)});
|
||||
@ -0,0 +1,256 @@
|
||||
/**
|
||||
* @license Highcharts JS v5.0.2 (2016-10-26)
|
||||
* X-range series
|
||||
*
|
||||
* (c) 2010-2016 Torstein Honsi, Lars A. V. Cabrera
|
||||
*
|
||||
* --- WORK IN PROGRESS ---
|
||||
*
|
||||
* 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-2016 Highsoft AS
|
||||
* Authors: Torstein Honsi, Lars A. V. Cabrera
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var defaultPlotOptions = H.getOptions().plotOptions,
|
||||
color = H.Color,
|
||||
columnType = H.seriesTypes.column,
|
||||
each = H.each,
|
||||
extendClass = H.extendClass,
|
||||
isNumber = H.isNumber,
|
||||
isObject = H.isObject,
|
||||
merge = H.merge,
|
||||
pick = H.pick,
|
||||
seriesTypes = H.seriesTypes,
|
||||
stop = H.stop,
|
||||
wrap = H.wrap,
|
||||
Axis = H.Axis,
|
||||
Point = H.Point,
|
||||
Series = H.Series,
|
||||
pointFormat = '<span style="color:{point.color}">' +
|
||||
'\u25CF' +
|
||||
'</span> {series.name}: <b>{point.yCategory}</b><br/>',
|
||||
xrange = 'xrange';
|
||||
|
||||
defaultPlotOptions.xrange = merge(defaultPlotOptions.column, {
|
||||
tooltip: {
|
||||
pointFormat: pointFormat
|
||||
}
|
||||
});
|
||||
seriesTypes.xrange = extendClass(columnType, {
|
||||
pointClass: extendClass(Point, {
|
||||
// Add x2 and yCategory to the available properties for tooltip formats
|
||||
getLabelConfig: function() {
|
||||
var cfg = Point.prototype.getLabelConfig.call(this);
|
||||
|
||||
cfg.x2 = this.x2;
|
||||
cfg.yCategory = this.yCategory = this.series.yAxis.categories && this.series.yAxis.categories[this.y];
|
||||
return cfg;
|
||||
}
|
||||
}),
|
||||
type: xrange,
|
||||
forceDL: true,
|
||||
parallelArrays: ['x', 'x2', 'y'],
|
||||
requireSorting: false,
|
||||
animate: seriesTypes.line.prototype.animate,
|
||||
|
||||
/**
|
||||
* Borrow the column series metrics, but with swapped axes. This gives free access
|
||||
* to features like groupPadding, grouping, pointWidth etc.
|
||||
*/
|
||||
getColumnMetrics: function() {
|
||||
var metrics,
|
||||
chart = this.chart;
|
||||
|
||||
function swapAxes() {
|
||||
each(chart.series, function(s) {
|
||||
var xAxis = s.xAxis;
|
||||
s.xAxis = s.yAxis;
|
||||
s.yAxis = xAxis;
|
||||
});
|
||||
}
|
||||
|
||||
swapAxes();
|
||||
|
||||
this.yAxis.closestPointRange = 1;
|
||||
metrics = columnType.prototype.getColumnMetrics.call(this);
|
||||
|
||||
swapAxes();
|
||||
|
||||
return metrics;
|
||||
},
|
||||
|
||||
/**
|
||||
* Override cropData to show a point where x is outside visible range
|
||||
* but x2 is outside.
|
||||
*/
|
||||
cropData: function(xData, yData, min, max) {
|
||||
|
||||
// Replace xData with x2Data to find the appropriate cropStart
|
||||
var cropData = Series.prototype.cropData,
|
||||
crop = cropData.call(this, this.x2Data, yData, min, max);
|
||||
|
||||
// Re-insert the cropped xData
|
||||
crop.xData = xData.slice(crop.start, crop.end);
|
||||
|
||||
return crop;
|
||||
},
|
||||
|
||||
translate: function() {
|
||||
columnType.prototype.translate.apply(this, arguments);
|
||||
var series = this,
|
||||
xAxis = series.xAxis,
|
||||
metrics = series.columnMetrics,
|
||||
minPointLength = series.options.minPointLength || 0;
|
||||
|
||||
each(series.points, function(point) {
|
||||
var plotX = point.plotX,
|
||||
posX = pick(point.x2, point.x + (point.len || 0)),
|
||||
plotX2 = xAxis.toPixels(posX, true),
|
||||
width = plotX2 - plotX,
|
||||
widthDifference,
|
||||
shapeArgs,
|
||||
partialFill;
|
||||
|
||||
if (minPointLength) {
|
||||
widthDifference = minPointLength - width;
|
||||
if (widthDifference < 0) {
|
||||
widthDifference = 0;
|
||||
}
|
||||
plotX -= widthDifference / 2;
|
||||
plotX2 += widthDifference / 2;
|
||||
}
|
||||
|
||||
plotX = Math.max(plotX, -10);
|
||||
plotX2 = Math.min(Math.max(plotX2, -10), xAxis.len + 10);
|
||||
|
||||
point.shapeArgs = {
|
||||
x: plotX,
|
||||
y: point.plotY + metrics.offset,
|
||||
width: plotX2 - plotX,
|
||||
height: metrics.width
|
||||
};
|
||||
point.tooltipPos[0] += width / 2;
|
||||
point.tooltipPos[1] -= metrics.width / 2;
|
||||
|
||||
// Add a partShapeArgs to the point, based on the shapeArgs property
|
||||
partialFill = point.partialFill;
|
||||
if (partialFill) {
|
||||
// Get the partial fill amount
|
||||
if (isObject(partialFill)) {
|
||||
partialFill = partialFill.amount;
|
||||
}
|
||||
// If it was not a number, assume 0
|
||||
if (!isNumber(partialFill)) {
|
||||
partialFill = 0;
|
||||
}
|
||||
shapeArgs = point.shapeArgs;
|
||||
point.partShapeArgs = {
|
||||
x: shapeArgs.x,
|
||||
y: shapeArgs.y + 1,
|
||||
width: shapeArgs.width * partialFill,
|
||||
height: shapeArgs.height - 2
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
drawPoints: function() {
|
||||
var series = this,
|
||||
chart = this.chart,
|
||||
options = series.options,
|
||||
renderer = chart.renderer,
|
||||
animationLimit = options.animationLimit || 250,
|
||||
verb = chart.pointCount < animationLimit ? 'animate' : 'attr';
|
||||
|
||||
// draw the columns
|
||||
each(series.points, function(point) {
|
||||
var plotY = point.plotY,
|
||||
graphic = point.graphic,
|
||||
type = point.shapeType,
|
||||
shapeArgs = point.shapeArgs,
|
||||
partShapeArgs = point.partShapeArgs,
|
||||
seriesOpts = series.options,
|
||||
pfOptions = point.partialFill,
|
||||
fill,
|
||||
state = point.selected && 'select',
|
||||
cutOff = options.stacking && !options.borderRadius;
|
||||
|
||||
if (isNumber(plotY) && point.y !== null) {
|
||||
if (graphic) { // update
|
||||
stop(graphic);
|
||||
point.graphicOriginal[verb](
|
||||
merge(shapeArgs)
|
||||
);
|
||||
if (partShapeArgs) {
|
||||
point.graphicOverlay[verb](
|
||||
merge(partShapeArgs)
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
point.graphic = graphic = renderer.g('point')
|
||||
.attr({
|
||||
'class': point.getClassName()
|
||||
})
|
||||
.add(point.group || series.group);
|
||||
|
||||
point.graphicOriginal = renderer[type](shapeArgs)
|
||||
.addClass('highcharts-partfill-original')
|
||||
.add(graphic);
|
||||
if (partShapeArgs) {
|
||||
point.graphicOverlay = renderer[type](partShapeArgs)
|
||||
.addClass('highcharts-partfill-overlay')
|
||||
.add(graphic);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else if (graphic) {
|
||||
point.graphic = graphic.destroy(); // #1269
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Max x2 should be considered in xAxis extremes
|
||||
*/
|
||||
wrap(Axis.prototype, 'getSeriesExtremes', function(proceed) {
|
||||
var axis = this,
|
||||
series = axis.series,
|
||||
dataMax,
|
||||
modMax;
|
||||
|
||||
proceed.call(this);
|
||||
if (axis.isXAxis && series.type === xrange) {
|
||||
dataMax = pick(axis.dataMax, Number.MIN_VALUE);
|
||||
each(this.series, function(series) {
|
||||
each(series.x2Data || [], function(val) {
|
||||
if (val > dataMax) {
|
||||
dataMax = val;
|
||||
modMax = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
if (modMax) {
|
||||
axis.dataMax = dataMax;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}(Highcharts));
|
||||
}));
|
||||
Reference in New Issue
Block a user