diff --git a/README.md b/README.md index 6baa7c7..3810072 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,12 @@ More information at the website: https://frappe.github.io/charts ### Usage + +Install +``` +npm install frappe-charts +``` + Include it in your html: ``` diff --git a/dist/frappe-charts.min.js b/dist/frappe-charts.min.js index afbe7b9..dbd2e6d 100644 --- a/dist/frappe-charts.min.js +++ b/dist/frappe-charts.min.js @@ -1,2 +1,2 @@ -var Chart=function(){"use strict";function t(t,e){return"string"==typeof t?(e||document).querySelector(t):t||null}function e(t){return parseFloat(t.toFixed(2))}function i(t,e){if(t.length!==e.length)return!1;var i=!0;return t.map(function(t,a){e[a]!==t&&(i=!1)}),i}!function(t,e){if("undefined"==typeof document)return e;t=t||"";var i=document.head||document.getElementsByTagName("head")[0],a=document.createElement("style");a.type="text/css",i.appendChild(a),a.styleSheet?a.styleSheet.cssText=t:a.appendChild(document.createTextNode(t))}('.chart-container{font-family:Roboto,Geneva,Tahoma,sans-serif}.chart-container .graph-focus-margin{margin:0 5%}.chart-container>.title{margin-top:25px;margin-left:25px;text-align:left;font-weight:400;font-size:12px;color:#6c7680}.chart-container .graphics{margin-top:10px;padding-top:10px;padding-bottom:10px;position:relative}.chart-container .graph-stats-group{-ms-flex-pack:distribute;-webkit-box-flex:1;-ms-flex:1;flex:1}.chart-container .graph-stats-container,.chart-container .graph-stats-group{display:-webkit-box;display:-ms-flexbox;display:flex;justify-content:space-around}.chart-container .graph-stats-container{-ms-flex-pack:distribute;padding-top:10px}.chart-container .graph-stats-container .stats{padding-bottom:15px}.chart-container .graph-stats-container .stats-title{color:#8d99a6}.chart-container .graph-stats-container .stats-value{font-size:20px;font-weight:300}.chart-container .graph-stats-container .stats-description{font-size:12px;color:#8d99a6}.chart-container .graph-stats-container .graph-data .stats-value{color:#98d85b}.chart-container .axis,.chart-container .chart-label{font-size:11px;fill:#555b51}.chart-container .axis line,.chart-container .chart-label line{stroke:#dadada}.chart-container .percentage-graph .progress{margin-bottom:0}.chart-container .data-points circle{stroke:#fff;stroke-width:2}.chart-container .path-group path{fill:none;stroke-opacity:1;stroke-width:2px}.chart-container line.dashed{stroke-dasharray:5,3}.chart-container .tick.x-axis-label{display:block}.chart-container .tick .specific-value{text-anchor:start}.chart-container .tick .y-value-text{text-anchor:end}.chart-container .tick .x-value-text{text-anchor:middle}.chart-container .progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.chart-container .progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#36414c;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;transition:width .6s ease}.graph-svg-tip{position:absolute;z-index:1;padding:10px;font-size:12px;color:#959da5;text-align:center;background:rgba(0,0,0,.8);border-radius:3px}.graph-svg-tip ol,.graph-svg-tip ul{padding-left:0;display:-webkit-box;display:-ms-flexbox;display:flex}.graph-svg-tip ul.data-point-list li{min-width:90px;-webkit-box-flex:1;-ms-flex:1;flex:1;font-weight:600}.graph-svg-tip strong{color:#dfe2e5;font-weight:600}.graph-svg-tip .svg-pointer{position:absolute;bottom:-10px;left:50%;width:5px;height:5px;margin:0 0 0 -5px;content:" ";border:5px solid transparent;border-top-color:rgba(0,0,0,.8)}.graph-svg-tip.comparison{padding:0;text-align:left;pointer-events:none}.graph-svg-tip.comparison .title{display:block;padding:10px;margin:0;font-weight:600;line-height:1;pointer-events:none}.graph-svg-tip.comparison ul{margin:0;white-space:nowrap;list-style:none}.graph-svg-tip.comparison li{display:inline-block;padding:5px 10px}.indicator,.indicator-right{background:none;font-size:12px;vertical-align:middle;font-weight:700;color:#6c7680}.indicator-right:after,.indicator:before{content:"";display:inline-block;height:8px;width:8px;border-radius:8px}.indicator:before{margin:0 4px 0 0}.indicator-right:after{margin:0 0 0 4px}.background.grey,.indicator-right.grey:after,.indicator.grey:before{background:#bdd3e6}.background.light-grey,.indicator-right.light-grey:after,.indicator.light-grey:before{background:#f0f4f7}.background.blue,.indicator-right.blue:after,.indicator.blue:before{background:#5e64ff}.background.red,.indicator-right.red:after,.indicator.red:before{background:#ff5858}.background.green,.indicator-right.green:after,.indicator.green:before{background:#28a745}.background.light-green,.indicator-right.light-green:after,.indicator.light-green:before{background:#98d85b}.background.orange,.indicator-right.orange:after,.indicator.orange:before{background:#ffa00a}.background.violet,.indicator-right.violet:after,.indicator.violet:before{background:#743ee2}.background.dark-grey,.indicator-right.dark-grey:after,.indicator.dark-grey:before{background:#b8c2cc}.background.black,.indicator-right.black:after,.indicator.black:before{background:#36414c}.background.yellow,.indicator-right.yellow:after,.indicator.yellow:before{background:#feef72}.background.light-blue,.indicator-right.light-blue:after,.indicator.light-blue:before{background:#7cd6fd}.background.purple,.indicator-right.purple:after,.indicator.purple:before{background:#b554ff}.background.magenta,.indicator-right.magenta:after,.indicator.magenta:before{background:#ffa3ef}.stroke.grey{stroke:#bdd3e6}.stroke.light-grey{stroke:#f0f4f7}.stroke.blue{stroke:#5e64ff}.stroke.red{stroke:#ff5858}.stroke.light-green{stroke:#98d85b}.stroke.green{stroke:#28a745}.stroke.orange{stroke:#ffa00a}.stroke.violet{stroke:#743ee2}.stroke.dark-grey{stroke:#b8c2cc}.stroke.black{stroke:#36414c}.stroke.yellow{stroke:#feef72}.stroke.light-blue{stroke:#7cd6fd}.stroke.purple{stroke:#b554ff}.stroke.magenta{stroke:#ffa3ef}.fill.grey{fill:#bdd3e6}.fill.light-grey{fill:#f0f4f7}.fill.blue{fill:#5e64ff}.fill.red{fill:#ff5858}.fill.light-green{fill:#98d85b}.fill.green{fill:#28a745}.fill.orange{fill:#ffa00a}.fill.violet{fill:#743ee2}.fill.dark-grey{fill:#b8c2cc}.fill.black{fill:#36414c}.fill.yellow{fill:#feef72}.fill.light-blue{fill:#7cd6fd}.fill.purple{fill:#b554ff}.fill.magenta{fill:#ffa3ef}.border-top.grey{border-top:3px solid #bdd3e6}.border-top.light-grey{border-top:3px solid #f0f4f7}.border-top.blue{border-top:3px solid #5e64ff}.border-top.red{border-top:3px solid #ff5858}.border-top.light-green{border-top:3px solid #98d85b}.border-top.green{border-top:3px solid #28a745}.border-top.orange{border-top:3px solid #ffa00a}.border-top.violet{border-top:3px solid #743ee2}.border-top.dark-grey{border-top:3px solid #b8c2cc}.border-top.black{border-top:3px solid #36414c}.border-top.yellow{border-top:3px solid #feef72}.border-top.light-blue{border-top:3px solid #7cd6fd}.border-top.purple{border-top:3px solid #b554ff}.border-top.magenta{border-top:3px solid #ffa3ef}.stop-color.grey{stop-color:#bdd3e6}.stop-color.light-grey{stop-color:#f0f4f7}.stop-color.blue{stop-color:#5e64ff}.stop-color.red{stop-color:#ff5858}.stop-color.light-green{stop-color:#98d85b}.stop-color.green{stop-color:#28a745}.stop-color.orange{stop-color:#ffa00a}.stop-color.violet{stop-color:#743ee2}.stop-color.dark-grey{stop-color:#b8c2cc}.stop-color.black{stop-color:#36414c}.stop-color.yellow{stop-color:#feef72}.stop-color.light-blue{stop-color:#7cd6fd}.stop-color.purple{stop-color:#b554ff}.stop-color.magenta{stop-color:#ffa3ef}',void 0);!function(){function t(t){this.value=t}function e(e){function i(s,n){try{var r=e[s](n),o=r.value;o instanceof t?Promise.resolve(o.value).then(function(t){i("next",t)},function(t){i("throw",t)}):a(r.done?"return":"normal",r.value)}catch(t){a("throw",t)}}function a(t,e){switch(t){case"return":s.resolve({value:e,done:!0});break;case"throw":s.reject(e);break;default:s.resolve({value:e,done:!1})}(s=s.next)?i(s.key,s.arg):n=null}var s,n;this._invoke=function(t,e){return new Promise(function(a,r){var o={key:t,arg:e,resolve:a,reject:r,next:null};n?n=n.next=o:(s=n=o,i(t,e))})},"function"!=typeof e.return&&(this.return=void 0)}"function"==typeof Symbol&&Symbol.asyncIterator&&(e.prototype[Symbol.asyncIterator]=function(){return this}),e.prototype.next=function(t){return this._invoke("next",t)},e.prototype.throw=function(t){return this._invoke("throw",t)},e.prototype.return=function(t){return this._invoke("return",t)}}();var a=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},s=function(){function t(t,e){for(var i=0;i3&&void 0!==arguments[3]?arguments[3]:"linear",s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:void 0,n=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{},r={ease:"0.25 0.1 0.25 1",linear:"0 0 1 1",easein:"0.1 0.8 0.2 1",easeout:"0 0 0.58 1",easeinout:"0.42 0 0.58 1"},o=t.cloneNode(!0),l=t.cloneNode(!0);for(var h in e){var _=void 0;_="transform"===h?document.createElementNS("http://www.w3.org/2000/svg","animateTransform"):document.createElementNS("http://www.w3.org/2000/svg","animate");var u=n[h]||t.getAttribute(h),c=e[h],p={attributeName:h,from:u,to:c,begin:"0s",dur:i/1e3+"s",values:u+";"+c,keySplines:r[a],keyTimes:"0;1",calcMode:"spline",fill:"freeze"};s&&(p.type=s);for(var d in p)_.setAttribute(d,p[d]);o.appendChild(_),s?l.setAttribute(h,"translate("+c+")"):l.setAttribute(h,c)}return[o,l]},t.offset=function(t){var e=t.getBoundingClientRect();return{top:e.top+(document.documentElement.scrollTop||document.body.scrollTop),left:e.left+(document.documentElement.scrollLeft||document.body.scrollLeft)}},t.isElementInViewport=function(t){var e=t.getBoundingClientRect();return e.top>=0&&e.left>=0&&e.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&e.right<=(window.innerWidth||document.documentElement.clientWidth)},t.bind=function(t,e){if(t)for(var i in e){var a=e[i];i.split(/\s+/).forEach(function(e){t.addEventListener(e,a)})}},t.unbind=function(t,e){if(t)for(var i in e){var a=e[i];i.split(/\s+/).forEach(function(e){t.removeEventListener(e,a)})}},t.fire=function(t,e,i){var a=document.createEvent("HTMLEvents");a.initEvent(e,!0,!0);for(var s in i)a[s]=i[s];return t.dispatchEvent(a)};var _=function(){function e(t){var i=t.parent,s=void 0===i?null:i;a(this,e),this.parent=s,this.title_name="",this.title_value="",this.list_values=[],this.title_value_first=0,this.x=0,this.y=0,this.top=0,this.left=0,this.setup()}return s(e,[{key:"setup",value:function(){this.make_tooltip()}},{key:"refresh",value:function(){this.fill(),this.calc_position()}},{key:"make_tooltip",value:function(){var e=this;this.container=t.create("div",{inside:this.parent,className:"graph-svg-tip comparison",innerHTML:'\n\t\t\t\t\n\t\t\t\t
'}),this.hide_tip(),this.title=this.container.querySelector(".title"),this.data_point_list=this.container.querySelector(".data-point-list"),this.parent.addEventListener("mouseleave",function(){e.hide_tip()})}},{key:"fill",value:function(){var e=this,i=void 0;i=this.title_value_first?""+this.title_value+""+this.title_name:this.title_name+""+this.title_value+"",this.title.innerHTML=i,this.data_point_list.innerHTML="",this.list_values.map(function(i){var a=t.create("li",{className:"border-top "+(i.color||"black"),innerHTML:''+(i.value?i.value:"")+"\n\t\t\t\t\t"+(i.title?i.title:"")});e.data_point_list.appendChild(a)})}},{key:"calc_position",value:function(){this.top=this.y-this.container.offsetHeight,this.left=this.x-this.container.offsetWidth/2;var t=this.parent.offsetWidth-this.container.offsetWidth,e=this.container.querySelector(".svg-pointer");if(this.left<0)e.style.left="calc(50% - "+-1*this.left+"px)",this.left=0;else if(this.left>t){var i=this.left-t;e.style.left="calc(50% + "+i+"px)",this.left=t}else e.style.left="50%"}},{key:"set_values",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"",s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:[],n=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0;this.title_name=i,this.title_value=a,this.list_values=s,this.x=t,this.y=e,this.title_value_first=n,this.refresh()}},{key:"hide_tip",value:function(){this.container.style.top="0px",this.container.style.left="0px",this.container.style.opacity="0"}},{key:"show_tip",value:function(){this.container.style.top=this.top+"px",this.container.style.left=this.left+"px",this.container.style.opacity="1"}}]),e}(),u=function(){function e(t){var i=t.parent,s=void 0===i?"":i,n=t.height,r=void 0===n?240:n,o=t.title,l=void 0===o?"":o,h=t.subtitle,_=void 0===h?"":h,u=t.data,c=void 0===u?{}:u,p=t.format_lambdas,d=void 0===p?{}:p,f=t.summary,g=void 0===f?[]:f,v=t.is_navigable,y=void 0===v?0:v,m=t.has_legend,x=void 0===m?0:m;a(this,e),this.raw_chart_args=arguments[0],this.parent=document.querySelector(s),this.title=l,this.subtitle=_,this.data=c,this.format_lambdas=d,this.specific_values=c.specific_values||[],this.summary=g,this.is_navigable=y,this.is_navigable&&(this.current_index=0),this.has_legend=x,this.chart_types=["line","scatter","bar","percentage","heatmap"],this.set_margins(r)}return s(e,[{key:"get_different_chart",value:function(t){if(this.chart_types.includes(t)||console.error("'"+t+"' is not a valid chart type."),t!==this.type){return{bar:["line","scatter","percentage"],line:["scatter","bar","percentage"],scatter:["line","bar","percentage"],percentage:["bar","line","scatter"],heatmap:[]}[this.type].includes(t)||console.error("'"+this.type+"' chart cannot be converted to a '"+t+"' chart."),new y({parent:this.raw_chart_args.parent,title:this.title,data:this.raw_chart_args.data,type:t,height:this.raw_chart_args.height})}}},{key:"set_margins",value:function(t){this.base_height=t,this.height=t-40,this.translate_x=60,this.translate_y=10}},{key:"setup",value:function(){this.bind_window_events(),this.refresh(!0)}},{key:"bind_window_events",value:function(){var t=this;window.addEventListener("resize",function(){return t.refresh()}),window.addEventListener("orientationchange",function(){return t.refresh()})}},{key:"refresh",value:function(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.setup_base_values(),this.set_width(),this.setup_container(),this.setup_components(),this.setup_values(),this.setup_utils(),this.make_graph_components(t),this.make_tooltip(),this.summary.length>0?this.show_custom_summary():this.show_summary(),this.is_navigable&&this.setup_navigation(t)}},{key:"set_width",value:function(){var t=this,e=0;this.specific_values.map(function(i){t.get_strwidth(i.title)>e&&(e=t.get_strwidth(i.title)-40)}),this.base_width=this.parent.offsetWidth-e,this.width=this.base_width-2*this.translate_x}},{key:"setup_base_values",value:function(){}},{key:"setup_container",value:function(){this.container=t.create("div",{className:"chart-container",innerHTML:'
'+this.title+'
\n\t\t\t\t
'+this.subtitle+'
\n\t\t\t\t
\n\t\t\t\t
'}),this.parent.innerHTML="",this.parent.appendChild(this.container),this.chart_wrapper=this.container.querySelector(".frappe-chart"),this.stats_wrapper=this.container.querySelector(".graph-stats-container"),this.make_chart_area(),this.make_draw_area()}},{key:"make_chart_area",value:function(){return this.svg=t.createSVG("svg",{className:"chart",inside:this.chart_wrapper,width:this.base_width,height:this.base_height}),this.svg_defs=t.createSVG("defs",{inside:this.svg}),this.svg}},{key:"make_draw_area",value:function(){this.draw_area=t.createSVG("g",{className:this.type+"-chart",inside:this.svg,transform:"translate("+this.translate_x+", "+this.translate_y+")"})}},{key:"setup_components",value:function(){}},{key:"make_tooltip",value:function(){this.tip=new _({parent:this.chart_wrapper}),this.bind_tooltip()}},{key:"show_summary",value:function(){}},{key:"show_custom_summary",value:function(){var e=this;this.summary.map(function(i){var a=t.create("div",{className:"stats",innerHTML:''+i.title+": "+i.value+""});e.stats_wrapper.appendChild(a)})}},{key:"setup_navigation",value:function(){var e=this,i=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.make_overlay(),i&&(this.bind_overlay(),document.addEventListener("keydown",function(i){t.isElementInViewport(e.chart_wrapper)&&("37"==(i=i||window.event).keyCode?e.on_left_arrow():"39"==i.keyCode?e.on_right_arrow():"38"==i.keyCode?e.on_up_arrow():"40"==i.keyCode?e.on_down_arrow():"13"==i.keyCode&&e.on_enter_key())}))}},{key:"make_overlay",value:function(){}},{key:"bind_overlay",value:function(){}},{key:"bind_units",value:function(){}},{key:"on_left_arrow",value:function(){}},{key:"on_right_arrow",value:function(){}},{key:"on_up_arrow",value:function(){}},{key:"on_down_arrow",value:function(){}},{key:"on_enter_key",value:function(){}},{key:"get_data_point",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.current_index,e={index:t},i=this.y[0];return["svg_units","y_tops","values"].map(function(a){var s=a.slice(0,a.length-1);e[s]=i[a][t]}),e.label=this.x[t],e}},{key:"update_current_data_point",value:function(e){(e=parseInt(e))<0&&(e=0),e>=this.x.length&&(e=this.x.length-1),e!==this.current_index&&(this.current_index=e,t.fire(this.parent,"data-select",this.get_data_point()))}},{key:"get_strwidth",value:function(t){return 8*(t+"").length}},{key:"setup_utils",value:function(){}}]),e}(),c=function(_){function c(t){a(this,c);var e=o(this,(c.__proto__||Object.getPrototypeOf(c)).call(this,t));return e.x=e.data.labels,e.y=e.data.datasets,e.is_series=t.is_series,e.get_y_label=e.format_lambdas.y_label,e.get_y_tooltip=e.format_lambdas.y_tooltip,e.get_x_tooltip=e.format_lambdas.x_tooltip,e.colors=["green","blue","violet","red","orange","yellow","light-blue","light-green","purple","magenta"],e.zero_line=e.height,e}return r(c,u),s(c,[{key:"setup_values",value:function(){this.data.datasets.map(function(t){t.values=t.values.map(function(t){return isNaN(t)?0:t})}),this.setup_x(),this.setup_y()}},{key:"setup_x",value:function(){var t=this;this.set_avg_unit_width_and_x_offset(),this.x_axis_positions&&(this.x_old_axis_positions=this.x_axis_positions.slice()),this.x_axis_positions=this.x.map(function(i,a){return e(t.x_offset+a*t.avg_unit_width)}),this.x_old_axis_positions||(this.x_old_axis_positions=this.x_axis_positions.slice())}},{key:"setup_y",value:function(){this.y_axis_values&&(this.y_old_axis_values=this.y_axis_values.slice());var t=this.get_all_y_values();this.y_sums&&this.y_sums.length>0&&(t=t.concat(this.y_sums)),this.y_axis_values=this.get_y_axis_points(t),this.y_old_axis_values||(this.y_old_axis_values=this.y_axis_values.slice());var e=this.y_axis_values,i=e[e.length-1]-e[0];this.multiplier&&(this.old_multiplier=this.multiplier),this.multiplier=this.height/i,this.old_multiplier||(this.old_multiplier=this.multiplier);var a=e.indexOf(0),s=(e[1]-e[0])*this.multiplier;this.zero_line&&(this.old_zero_line=this.zero_line),this.zero_line=this.height-a*s,this.old_zero_line||(this.old_zero_line=this.zero_line)}},{key:"setup_components",value:function(){n(c.prototype.__proto__||Object.getPrototypeOf(c.prototype),"setup_components",this).call(this),this.setup_marker_components(),this.setup_aggregation_components(),this.setup_graph_components()}},{key:"setup_marker_components",value:function(){this.y_axis_group=t.createSVG("g",{className:"y axis",inside:this.draw_area}),this.x_axis_group=t.createSVG("g",{className:"x axis",inside:this.draw_area}),this.specific_y_group=t.createSVG("g",{className:"specific axis",inside:this.draw_area})}},{key:"setup_aggregation_components",value:function(){this.sum_group=t.createSVG("g",{className:"data-points",inside:this.draw_area}),this.average_group=t.createSVG("g",{className:"chart-area",inside:this.draw_area})}},{key:"setup_graph_components",value:function(){var e=this;this.svg_units_groups=[],this.y.map(function(i,a){e.svg_units_groups[a]=t.createSVG("g",{className:"data-points data-points-"+a,inside:e.draw_area})})}},{key:"make_graph_components",value:function(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.make_y_axis(),this.make_x_axis(),this.draw_graph(t),this.make_y_specifics()}},{key:"make_x_axis",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],i=void 0,a=void 0,s=void 0,n="";if("span"===this.x_axis_mode?(i=-7,a=this.height+15,s=this.height+25):"tick"===this.x_axis_mode&&(i=this.height,a=6,s=9,n="x-axis-label"),this.x_axis_group.setAttribute("transform","translate(0,"+i+")"),e)this.make_anim_x_axis(a,s,n);else{var r=1.5*this.avg_unit_width,o=r/8;this.x_axis_group.textContent="",this.x.map(function(e,i){var l=t.get_strwidth(e);if(l>r)if(t.is_series){for(var h=1;l/h*2>r;)h++;if(i%h!=0)return}else e=e.slice(0,o-3)+" ...";t.x_axis_group.appendChild(t.make_x_line(a,s,e,"x-value-text",n,t.x_axis_positions[i]))})}}},{key:"make_y_axis",value:function(){var t=this;if(arguments.length>0&&void 0!==arguments[0]&&arguments[0])return this.make_anim_y_axis(),void this.make_anim_y_specifics();var e=this.get_y_axis_line_props(),i=l(e,4),a=i[0],s=i[1],n=i[2],r=i[3];this.y_axis_group.textContent="",this.y_axis_values.map(function(e,i){t.y_axis_group.appendChild(t.make_y_line(r,a,s,e,"y-value-text",n,t.zero_line-e*t.multiplier,0===e&&0!==i))})}},{key:"get_y_axis_line_props",value:function(){if(arguments.length>0&&void 0!==arguments[0]&&arguments[0])return[this.width,this.width+5,"specific-value",0];var t=void 0,e="",i=0;return"span"===this.y_axis_mode?(t=this.width+6,i=-6):"tick"===this.y_axis_mode&&(t=-6,e="y-axis-label"),[t,-9,e,i]}},{key:"draw_graph",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];!this.raw_chart_args.hasOwnProperty("init")||this.raw_chart_args.init?e?this.draw_new_graph_and_animate():this.y.map(function(e,i){e.svg_units=[],t.make_path&&t.make_path(e,i,t.x_axis_positions,e.y_tops,e.color||t.colors[i]),t.make_new_units(e,i)}):this.y.map(function(e,i){e.svg_units=[],t.make_path&&t.make_path(e,i,t.x_axis_positions,e.y_tops,e.color||t.colors[i]),t.make_new_units(e,i),t.calc_y_dependencies()})}},{key:"draw_new_graph_and_animate",value:function(){var t=this,e=[];this.y.map(function(i,a){i.y_tops=new Array(i.values.length).fill(t.zero_line),e.push({values:i.values}),i.svg_units=[],t.make_path&&t.make_path(i,a,t.x_axis_positions,i.y_tops,i.color||t.colors[a]),t.make_new_units(i,a)}),setTimeout(function(){t.update_values(e)},350)}},{key:"setup_navigation",value:function(t){var e=this;t?setTimeout(function(){n(c.prototype.__proto__||Object.getPrototypeOf(c.prototype),"setup_navigation",e).call(e,t)},500):n(c.prototype.__proto__||Object.getPrototypeOf(c.prototype),"setup_navigation",this).call(this,t)}},{key:"make_new_units",value:function(t,e){this.make_new_units_for_dataset(this.x_axis_positions,t.y_tops,t.color||this.colors[e],e,this.y.length)}},{key:"make_new_units_for_dataset",value:function(t,e,i,a,s,n,r,o){var l=this;n||(n=this.svg_units_groups[a]),r||(r=this.y[a].svg_units),o||(o=this.unit_args),n.textContent="",r.length=0,e.map(function(e,h){var _=l.draw[o.type](t[h],e,o.args,i,h,a,s);n.appendChild(_),r.push(_)}),this.is_navigable&&this.bind_units(r)}},{key:"make_y_specifics",value:function(){var t=this;this.specific_y_group.textContent="",this.specific_values.map(function(e){t.specific_y_group.appendChild(t.make_y_line(0,t.width,t.width+5,e.title.toUpperCase(),"specific-value","specific-value",t.zero_line-e.value*t.multiplier,!1,e.line_type))})}},{key:"bind_tooltip",value:function(){var e=this;this.chart_wrapper.addEventListener("mousemove",function(i){var a=t.offset(e.chart_wrapper),s=i.pageX-a.left-e.translate_x;i.pageY-a.top-e.translate_y=0;i--){var a=this.x_axis_positions[i];if(t>a-this.avg_unit_width/2){var s=a+this.translate_x,n=this.y_min_tops[i]+this.translate_y,r=this.x.formatted&&this.x.formatted.length>0?this.x.formatted[i]:this.x[i],o=this.y.map(function(t,a){return{title:t.title,value:t.formatted?t.formatted[i]:t.values[i],color:t.color||e.colors[a]}});this.tip.set_values(s,n,r,"",o),this.tip.show_tip();break}}}},{key:"show_sums",value:function(){var t=this;this.updating=!0,this.y_sums=new Array(this.x_axis_positions.length).fill(0),this.y.map(function(e){e.values.map(function(e,i){t.y_sums[i]+=e})}),this.update_values(),this.sum_units=[],this.make_new_units_for_dataset(this.x_axis_positions,this.y_sums.map(function(i){return e(t.zero_line-i*t.multiplier)}),"light-grey",0,1,this.sum_group,this.sum_units),this.updating=!1}},{key:"hide_sums",value:function(){this.updating||(this.y_sums=[],this.sum_group.textContent="",this.sum_units=[],this.update_values())}},{key:"show_averages",value:function(){var t=this;this.old_specific_values=this.specific_values.slice(),this.y.map(function(e,i){var a=0;e.values.map(function(t){a+=t});var s=a/e.values.length;t.specific_values.push({title:"AVG "+(i+1),line_type:"dashed",value:s,auto:1})}),this.update_values()}},{key:"hide_averages",value:function(){var t=this;this.old_specific_values=this.specific_values.slice();var e=[];this.specific_values.map(function(t,i){t.auto&&e.unshift(i)}),e.map(function(e){t.specific_values.splice(e,1)}),this.update_values()}},{key:"update_values",value:function(t,e){var a=this;e||(e=this.x),this.elements_to_animate=[],this.updating=!0,this.old_x_values=this.x.slice(),this.old_y_axis_tops=this.y.map(function(t){return t.y_tops.slice()}),this.old_y_values=this.y.map(function(t){return t.values}),this.no_of_extra_pts=e.length-this.x.length,t&&this.y.map(function(e,i){e.values=t[i].values}),e&&(this.x=e),this.setup_x(),this.setup_y(),i(this.x_old_axis_positions,this.x_axis_positions)||(this.make_x_axis(!0),setTimeout(function(){a.updating||a.make_x_axis()},350)),(!i(this.y_old_axis_values,this.y_axis_values)||this.old_specific_values&&!i(this.old_specific_values,this.specific_values))&&(this.make_y_axis(!0),setTimeout(function(){a.updating||(a.make_y_axis(),a.make_y_specifics())},350)),this.calc_y_dependencies(),this.animate_graphs(),this.run_animation(),this.updating=!1}},{key:"add_data_point",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.x.length,a=this.y.map(function(t){return{values:t.values}});a.map(function(e,a){e.values.splice(i,0,t[a])});var s=this.x.slice();s.splice(i,0,e),this.update_values(a,s)}},{key:"remove_data_point",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.x.length-1;if(!(this.x.length<3)){var e=this.y.map(function(t){return{values:t.values}});e.map(function(e){e.values.splice(t,1)});var i=this.x.slice();i.splice(t,1),this.update_values(e,i)}}},{key:"run_animation",value:function(){var e=this,i=t.runSVGAnimation(this.svg,this.elements_to_animate);this.svg.parentNode==this.chart_wrapper&&(this.chart_wrapper.removeChild(this.svg),this.chart_wrapper.appendChild(i)),setTimeout(function(){i.parentNode==e.chart_wrapper&&(e.chart_wrapper.removeChild(i),e.chart_wrapper.appendChild(e.svg))},250)}},{key:"animate_graphs",value:function(){var t=this;this.y.map(function(e,i){var a=t.calc_old_and_new_postions(e,i),s=l(a,4),n=s[0],r=s[1],o=s[2],h=s[3];t.no_of_extra_pts>=0&&(t.make_path&&t.make_path(e,i,n,r,e.color||t.colors[i]),t.make_new_units_for_dataset(n,r,e.color||t.colors[i],i,t.y.length)),e.path&&t.animate_path(e,i,n,r,o,h),t.animate_units(e,i,n,r,o,h)}),setTimeout(function(){t.y.map(function(e,i){t.make_path&&t.make_path(e,i,t.x_axis_positions,e.y_tops,e.color||t.colors[i]),t.make_new_units(e,i)})},400)}},{key:"animate_path",value:function(t,e,i,a,s,n){var r=n.map(function(t,e){return s[e]+","+t}).join("L"),o=[{unit:t.path,object:t,key:"path"},{d:"M"+r},350,"easein"];if(this.elements_to_animate.push(o),t.region_path){var l="0,"+this.zero_line+"L",h="L"+this.width+","+this.zero_line,_=[{unit:t.region_path,object:t,key:"region_path"},{d:"M"+l+r+h},350,"easein"];this.elements_to_animate.push(_)}}},{key:"animate_units",value:function(t,e,i,a,s,n){var r=this,o=this.unit_args.type;t.svg_units.map(function(i,a){void 0!==s[a]&&void 0!==n[a]&&r.elements_to_animate.push(r.animate[o]({unit:i,array:t.svg_units,index:a},s[a],n[a],e))})}},{key:"calc_old_and_new_postions",value:function(t,e){var i=this.x_old_axis_positions.slice(),a=this.x_axis_positions.slice(),s=this.old_y_axis_tops[e].slice(),n=t.y_tops.slice(),r=i[i.length-1],o=s[s.length-1],l=a[a.length-1],h=n[n.length-1];if(this.no_of_extra_pts>=0){var _=new Array(Math.abs(this.no_of_extra_pts)).fill(r),u=new Array(Math.abs(this.no_of_extra_pts)).fill(o);i=i.concat(_),s=s.concat(u)}else{var c=new Array(Math.abs(this.no_of_extra_pts)).fill(l),p=new Array(Math.abs(this.no_of_extra_pts)).fill(h);a=a.concat(c),n=n.concat(p)}return[i,s,a,n]}},{key:"make_anim_x_axis",value:function(t,e,i){var a=this,s=this.x_old_axis_positions,n=this.x_axis_positions,r=this.old_x_values,o=this.x,l=s[s.length-1];this.x_axis_group.textContent="",this.make_new_axis_anim_lines(s,n,r,o,l,function(s,n,r){"string"==typeof r&&(r=parseInt(r.substring(0,r.length-1)));var o=a.make_x_line(t,e,s,"x-value-text",i,n);a.x_axis_group.appendChild(o),a.elements_to_animate&&a.elements_to_animate.push([{unit:o,array:[0],index:0},{transform:r+", 0"},350,"easein","translate",{transform:n+", 0"}])})}},{key:"make_anim_y_axis",value:function(){var t=this,e=this.y_old_axis_values.map(function(e){return t.zero_line-e*t.multiplier}),i=this.y_axis_values.map(function(e){return t.zero_line-e*t.multiplier}),a=this.y_old_axis_values,s=this.y_axis_values,n=e[e.length-1];this.y_axis_group.textContent="",this.make_new_axis_anim_lines(e,i,a,s,n,this.add_and_animate_y_line.bind(this),this.y_axis_group)}},{key:"make_anim_y_specifics",value:function(){var t=this;this.specific_y_group.textContent="",this.specific_values.map(function(e){t.add_and_animate_y_line(e.title,t.old_zero_line-e.value*t.old_multiplier,t.zero_line-e.value*t.multiplier,0,t.specific_y_group,e.line_type,!0)})}},{key:"make_new_axis_anim_lines",value:function(t,e,i,a,s,n,r){var o=void 0,l=void 0,h=a.length-i.length;if(h>0)o=e.slice(0,t.length),l=a.slice(0,i.length);else{var _=new Array(Math.abs(h)).fill("");l=a.concat(_);var u=new Array(Math.abs(h)).fill(s+"F");o=e.concat(u)}if(l.map(function(e,i){n(e,t[i],o[i],i,r)}),h>0){var c=a.slice(i.length),p=e.slice(t.length);c.map(function(t,e){n(t,s,p[e],e,r)})}}},{key:"make_x_line",value:function(e,i,a,s,n,r){var o=t.createSVG("line",{x1:0,x2:0,y1:0,y2:e}),l=t.createSVG("text",{className:s,x:0,y:i,dy:".71em",innerHTML:a}),h=t.createSVG("g",{className:"tick "+n,transform:"translate("+r+", 0)"});return h.appendChild(o),h.appendChild(l),h}},{key:"make_y_line",value:function(e,i,a,s,n,r,o){var l=arguments.length>7&&void 0!==arguments[7]&&arguments[7],h=arguments.length>8&&void 0!==arguments[8]?arguments[8]:"",_=t.createSVG("line",{className:"dashed"===h?"dashed":"",x1:e,x2:i,y1:0,y2:0}),u=t.createSVG("text",{className:n,x:a,y:0,dy:".32em",innerHTML:s+""}),c=t.createSVG("g",{className:"tick "+r,transform:"translate(0, "+o+")","stroke-opacity":1});return l&&(_.style.stroke="rgba(27, 31, 35, 0.6)"),c.appendChild(_),c.appendChild(u),c}},{key:"add_and_animate_y_line",value:function(t,e,i,a,s,n){var r=arguments.length>6&&void 0!==arguments[6]&&arguments[6],o=!1;"string"==typeof i&&(i=parseInt(i.substring(0,i.length-1)),o=!0);var h={transform:"0, "+i},_={transform:"0, "+e};o&&(h["stroke-opacity"]=0);var u=this.get_y_axis_line_props(r),c=l(u,4),p=c[0],d=c[1],f=c[2],g=c[3],v=r?"specific-value":"y-value-text";t=r?(t+"").toUpperCase():t;var y=this.make_y_line(g,p,d,t,v,f,e,0===t&&0!==a,n);s.appendChild(y),this.elements_to_animate&&this.elements_to_animate.push([{unit:y,array:[0],index:0},h,350,"easein","translate",_])}},{key:"get_y_axis_points",value:function(t){var e=this,i=void 0,a=void 0,s=void 0,n=void 0,r=parseInt(Math.max.apply(Math,h(t))),o=parseInt(Math.min.apply(Math,h(t)));o>=0&&(o=0);var _=function(t,i){var a=void 0,s=void 0,n=void 0,r=void 0,o=void 0;if((t+"").length<=1)a=10,n=5;else{var h=e.calc_upper_bound_and_no_of_parts(t),_=l(h,2);a=_[0],n=_[1]}return o=a/n,r=e.calc_no_of_parts(i,o),s=r*o,[a,s,n,r,o]},u=-1*o;if(u<=r){var c=_(r,u),p=l(c,5);i=p[1],a=p[2],s=p[3],n=p[4],0===u&&(i=0,s=0)}else{var d=_(u,r),f=l(d,5);i=f[0],s=f[2],a=f[3],n=f[4]}a%2!=0&&s>0&&a++,s%2!=0&&(s++,i+=n);var g=a+s;return g>5&&(g/=2,n*=2,a/=2),r<(a-1)*n&&g--,this.get_intervals(-1*i,n,g)}},{key:"get_intervals",value:function(t,e,i){for(var a=[],s=0;s<=i;s++)a.push(t),t+=e;return a}},{key:"calc_upper_bound_and_no_of_parts",value:function(t){var e=Math.pow(10,(t+"").length-1),i=this.calc_no_of_parts(t,e);return[e*i,i]}},{key:"calc_no_of_parts",value:function(t,e){var i=Math.ceil(t/e);return i%2!=0&&i++,i}},{key:"get_optimal_no_of_parts",value:function(t){return t<5?t:t/2}},{key:"set_avg_unit_width_and_x_offset",value:function(){this.avg_unit_width=this.width/(this.x.length-1),this.x_offset=0}},{key:"get_all_y_values",value:function(){var t=[];return this.y.map(function(e){t=t.concat(e.values)}),t.concat(this.specific_values.map(function(t){return t.value}))}},{key:"calc_y_dependencies",value:function(){var t=this;this.y_min_tops=new Array(this.x_axis_positions.length).fill(9999),this.y.map(function(i){i.y_tops=i.values.map(function(i){return e(t.zero_line-i*t.multiplier)}),i.y_tops.map(function(e,i){e1&&void 0!==arguments[1]&&arguments[1],a="path-fill-gradient-"+e,s=t.createSVG("linearGradient",{inside:this.svg_defs,id:a,x1:0,x2:0,y1:0,y2:1}),n=function(e,i,a,s){t.createSVG("stop",{className:"stop-color "+a,inside:e,offset:i,"stop-opacity":s})},r=[1,.6,.2];return i&&(r=[.4,.2,0]),n(s,"0%",e,r[0]),n(s,"50%",e,r[1]),n(s,"100%",e,r[2]),a}}]),i}(),f=function(t){function e(t){a(this,e);var i=o(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,t));return i.type="scatter",t.dot_radius?i.dot_radius=t.dot_radius:i.dot_radius=8,i.setup(),i}return r(e,d),s(e,[{key:"setup_graph_components",value:function(){this.setup_path_groups(),n(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"setup_graph_components",this).call(this)}},{key:"setup_path_groups",value:function(){}},{key:"setup_values",value:function(){n(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"setup_values",this).call(this),this.unit_args={type:"dot",args:{radius:this.dot_radius}}}},{key:"make_paths",value:function(){}},{key:"make_path",value:function(){}}]),e}(),g=function(e){function i(t){a(this,i);var e=o(this,(i.__proto__||Object.getPrototypeOf(i)).call(this,t));return e.type="percentage",e.get_y_label=e.format_lambdas.y_label,e.get_x_tooltip=e.format_lambdas.x_tooltip,e.get_y_tooltip=e.format_lambdas.y_tooltip,e.max_slices=10,e.max_legend_points=6,e.colors=t.colors,(!e.colors||e.colors.length0}),i=e;if(e.length>this.max_slices){e.sort(function(t,e){return e[0]-t[0]}),i=e.slice(0,this.max_slices-1);var a=0;e.slice(this.max_slices-1).map(function(t){a+=t[0]}),i.push([a,"Rest"]),this.colors[this.max_slices-1]="grey"}this.labels=[],i.map(function(e){t.slice_totals.push(e[0]),t.labels.push(e[1])}),this.legend_totals=this.slice_totals.slice(0,this.max_legend_points)}},{key:"setup_utils",value:function(){}},{key:"make_graph_components",value:function(){var e=this;this.grand_total=this.slice_totals.reduce(function(t,e){return t+e},0),this.slices=[],this.slice_totals.map(function(i,a){var s=t.create("div",{className:"progress-bar background "+e.colors[a],style:"width: "+100*i/e.grand_total+"%",inside:e.percentage_bar});e.slices.push(s)})}},{key:"bind_tooltip",value:function(){var e=this;this.slices.map(function(i,a){i.addEventListener("mouseenter",function(){var s=t.offset(e.chart_wrapper),n=t.offset(i),r=n.left-s.left+i.offsetWidth/2,o=n.top-s.top-6,l=(e.formatted_labels&&e.formatted_labels.length>0?e.formatted_labels[a]:e.labels[a])+": ",h=(100*e.slice_totals[a]/e.grand_total).toFixed(1);e.tip.set_values(r,o,l,h+"%"),e.tip.show_tip()})})}},{key:"show_summary",value:function(){var e=this,i=this.formatted_labels&&this.formatted_labels.length>0?this.formatted_labels:this.labels;this.legend_totals.map(function(a,s){a&&(t.create("div",{className:"stats",inside:e.stats_wrapper}).innerHTML='\n\t\t\t\t\t'+i[s]+":\n\t\t\t\t\t"+a+"\n\t\t\t\t")})}}]),i}(),v=function(e){function i(t){var e=t.start,s=void 0===e?"":e,n=t.domain,r=void 0===n?"":n,l=t.subdomain,h=void 0===l?"":l,_=t.data,u=void 0===_?{}:_,c=t.discrete_domains,p=void 0===c?0:c,d=t.count_label,f=void 0===d?"":d;a(this,i);var g=o(this,(i.__proto__||Object.getPrototypeOf(i)).call(this,arguments[0]));g.type="heatmap",g.domain=r,g.subdomain=h,g.data=u,g.discrete_domains=p,g.count_label=f;var v=new Date;return g.start=s||g.add_days(v,365),g.legend_colors=["#ebedf0","#c6e48b","#7bc96f","#239a3b","#196127"],g.translate_x=0,g.setup(),g}return r(i,u),s(i,[{key:"setup_base_values",value:function(){this.today=new Date,this.start||(this.start=new Date,this.start.setFullYear(this.start.getFullYear()-1)),this.first_week_start=new Date(this.start.toDateString()),this.last_week_start=new Date(this.today.toDateString()),7!==this.first_week_start.getDay()&&this.add_days(this.first_week_start,-1*this.first_week_start.getDay()),7!==this.last_week_start.getDay()&&this.add_days(this.last_week_start,-1*this.last_week_start.getDay()),this.no_of_cols=this.get_weeks_between(this.first_week_start+"",this.last_week_start+"")+1}},{key:"set_width",value:function(){this.base_width=12*this.no_of_cols,this.discrete_domains&&(this.base_width+=144)}},{key:"setup_components",value:function(){this.domain_label_group=t.createSVG("g",{className:"domain-label-group chart-label",inside:this.draw_area}),this.data_groups=t.createSVG("g",{className:"data-groups",inside:this.draw_area,transform:"translate(0, 20)"})}},{key:"setup_values",value:function(){this.domain_label_group.textContent="",this.data_groups.textContent="",this.distribution=this.get_distribution(this.data,this.legend_colors),this.month_names=["January","February","March","April","May","June","July","August","September","October","November","December"],this.render_all_weeks_and_store_x_values(this.no_of_cols)}},{key:"render_all_weeks_and_store_x_values",value:function(t){var e=new Date(this.first_week_start);this.week_col=0,this.current_month=e.getMonth(),this.months=[this.current_month+""],this.month_weeks={},this.month_start_points=[],this.month_weeks[this.current_month]=0,this.month_start_points.push(13);for(var i=0;i0&&void 0!==arguments[0]?arguments[0]:{},e=arguments[1],i=Object.keys(t).map(function(e){return t[e]}),a=Math.max.apply(Math,h(i)),s=1/(e.length-1),n=[];return e.map(function(t,e){var i=a*(s*e);n.push(i)}),n}},{key:"get_max_checkpoint",value:function(t,e){return e.filter(function(i,a){return 1===a?e[0]9?"":"0")+e,(i>9?"":"0")+i,t.getFullYear()].join("-")}},{key:"get_weeks_between",value:function(t,e){return Math.ceil(this.get_days_between(t,e)/7)}},{key:"get_days_between",value:function(t,e){return(this.treat_as_utc(e)-this.treat_as_utc(t))/864e5}},{key:"add_days",value:function(t,e){t.setDate(t.getDate()+e)}},{key:"get_month_name",value:function(){}}]),i}(),y=function t(e){return a(this,t),"line"===e.type?new d(arguments[0]):"bar"===e.type?new p(arguments[0]):"scatter"===e.type?new f(arguments[0]):"percentage"===e.type?new g(arguments[0]):"heatmap"===e.type?new v(arguments[0]):new d(arguments[0])};return y}(); +var Chart=function(){"use strict";function t(t,e){return"string"==typeof t?(e||document).querySelector(t):t||null}function e(t){return parseFloat(t.toFixed(2))}function i(t,e){if(t.length!==e.length)return!1;var i=!0;return t.map(function(t,a){e[a]!==t&&(i=!1)}),i}!function(t,e){if("undefined"==typeof document)return e;t=t||"";var i=document.head||document.getElementsByTagName("head")[0],a=document.createElement("style");a.type="text/css",i.appendChild(a),a.styleSheet?a.styleSheet.cssText=t:a.appendChild(document.createTextNode(t))}('.chart-container{font-family:Roboto,Geneva,Tahoma,sans-serif}.chart-container .graph-focus-margin{margin:0 5%}.chart-container>.title{margin-top:25px;margin-left:25px;text-align:left;font-weight:400;font-size:12px;color:#6c7680}.chart-container .graphics{margin-top:10px;padding-top:10px;padding-bottom:10px;position:relative}.chart-container .graph-stats-group{-ms-flex-pack:distribute;-webkit-box-flex:1;-ms-flex:1;flex:1}.chart-container .graph-stats-container,.chart-container .graph-stats-group{display:-webkit-box;display:-ms-flexbox;display:flex;justify-content:space-around}.chart-container .graph-stats-container{-ms-flex-pack:distribute;padding-top:10px}.chart-container .graph-stats-container .stats{padding-bottom:15px}.chart-container .graph-stats-container .stats-title{color:#8d99a6}.chart-container .graph-stats-container .stats-value{font-size:20px;font-weight:300}.chart-container .graph-stats-container .stats-description{font-size:12px;color:#8d99a6}.chart-container .graph-stats-container .graph-data .stats-value{color:#98d85b}.chart-container .axis,.chart-container .chart-label{font-size:11px;fill:#555b51}.chart-container .axis line,.chart-container .chart-label line{stroke:#dadada}.chart-container .percentage-graph .progress{margin-bottom:0}.chart-container .data-points circle{stroke:#fff;stroke-width:2}.chart-container .path-group path{fill:none;stroke-opacity:1;stroke-width:2px}.chart-container line.dashed{stroke-dasharray:5,3}.chart-container .tick.x-axis-label{display:block}.chart-container .tick .specific-value{text-anchor:start}.chart-container .tick .y-value-text{text-anchor:end}.chart-container .tick .x-value-text{text-anchor:middle}.chart-container .progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.chart-container .progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#36414c;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;transition:width .6s ease}.graph-svg-tip{position:absolute;z-index:1;padding:10px;font-size:12px;color:#959da5;text-align:center;background:rgba(0,0,0,.8);border-radius:3px}.graph-svg-tip ol,.graph-svg-tip ul{padding-left:0;display:-webkit-box;display:-ms-flexbox;display:flex}.graph-svg-tip ul.data-point-list li{min-width:90px;-webkit-box-flex:1;-ms-flex:1;flex:1;font-weight:600}.graph-svg-tip strong{color:#dfe2e5;font-weight:600}.graph-svg-tip .svg-pointer{position:absolute;bottom:-10px;left:50%;width:5px;height:5px;margin:0 0 0 -5px;content:" ";border:5px solid transparent;border-top-color:rgba(0,0,0,.8)}.graph-svg-tip.comparison{padding:0;text-align:left;pointer-events:none}.graph-svg-tip.comparison .title{display:block;padding:10px;margin:0;font-weight:600;line-height:1;pointer-events:none}.graph-svg-tip.comparison ul{margin:0;white-space:nowrap;list-style:none}.graph-svg-tip.comparison li{display:inline-block;padding:5px 10px}.indicator,.indicator-right{background:none;font-size:12px;vertical-align:middle;font-weight:700;color:#6c7680}.indicator-right:after,.indicator:before{content:"";display:inline-block;height:8px;width:8px;border-radius:8px}.indicator:before{margin:0 4px 0 0}.indicator-right:after{margin:0 0 0 4px}.background.grey,.indicator-right.grey:after,.indicator.grey:before{background:#bdd3e6}.background.light-grey,.indicator-right.light-grey:after,.indicator.light-grey:before{background:#f0f4f7}.background.blue,.indicator-right.blue:after,.indicator.blue:before{background:#5e64ff}.background.red,.indicator-right.red:after,.indicator.red:before{background:#ff5858}.background.green,.indicator-right.green:after,.indicator.green:before{background:#28a745}.background.light-green,.indicator-right.light-green:after,.indicator.light-green:before{background:#98d85b}.background.orange,.indicator-right.orange:after,.indicator.orange:before{background:#ffa00a}.background.violet,.indicator-right.violet:after,.indicator.violet:before{background:#743ee2}.background.dark-grey,.indicator-right.dark-grey:after,.indicator.dark-grey:before{background:#b8c2cc}.background.black,.indicator-right.black:after,.indicator.black:before{background:#36414c}.background.yellow,.indicator-right.yellow:after,.indicator.yellow:before{background:#feef72}.background.light-blue,.indicator-right.light-blue:after,.indicator.light-blue:before{background:#7cd6fd}.background.purple,.indicator-right.purple:after,.indicator.purple:before{background:#b554ff}.background.magenta,.indicator-right.magenta:after,.indicator.magenta:before{background:#ffa3ef}.stroke.grey{stroke:#bdd3e6}.stroke.light-grey{stroke:#f0f4f7}.stroke.blue{stroke:#5e64ff}.stroke.red{stroke:#ff5858}.stroke.light-green{stroke:#98d85b}.stroke.green{stroke:#28a745}.stroke.orange{stroke:#ffa00a}.stroke.violet{stroke:#743ee2}.stroke.dark-grey{stroke:#b8c2cc}.stroke.black{stroke:#36414c}.stroke.yellow{stroke:#feef72}.stroke.light-blue{stroke:#7cd6fd}.stroke.purple{stroke:#b554ff}.stroke.magenta{stroke:#ffa3ef}.fill.grey{fill:#bdd3e6}.fill.light-grey{fill:#f0f4f7}.fill.blue{fill:#5e64ff}.fill.red{fill:#ff5858}.fill.light-green{fill:#98d85b}.fill.green{fill:#28a745}.fill.orange{fill:#ffa00a}.fill.violet{fill:#743ee2}.fill.dark-grey{fill:#b8c2cc}.fill.black{fill:#36414c}.fill.yellow{fill:#feef72}.fill.light-blue{fill:#7cd6fd}.fill.purple{fill:#b554ff}.fill.magenta{fill:#ffa3ef}.border-top.grey{border-top:3px solid #bdd3e6}.border-top.light-grey{border-top:3px solid #f0f4f7}.border-top.blue{border-top:3px solid #5e64ff}.border-top.red{border-top:3px solid #ff5858}.border-top.light-green{border-top:3px solid #98d85b}.border-top.green{border-top:3px solid #28a745}.border-top.orange{border-top:3px solid #ffa00a}.border-top.violet{border-top:3px solid #743ee2}.border-top.dark-grey{border-top:3px solid #b8c2cc}.border-top.black{border-top:3px solid #36414c}.border-top.yellow{border-top:3px solid #feef72}.border-top.light-blue{border-top:3px solid #7cd6fd}.border-top.purple{border-top:3px solid #b554ff}.border-top.magenta{border-top:3px solid #ffa3ef}.stop-color.grey{stop-color:#bdd3e6}.stop-color.light-grey{stop-color:#f0f4f7}.stop-color.blue{stop-color:#5e64ff}.stop-color.red{stop-color:#ff5858}.stop-color.light-green{stop-color:#98d85b}.stop-color.green{stop-color:#28a745}.stop-color.orange{stop-color:#ffa00a}.stop-color.violet{stop-color:#743ee2}.stop-color.dark-grey{stop-color:#b8c2cc}.stop-color.black{stop-color:#36414c}.stop-color.yellow{stop-color:#feef72}.stop-color.light-blue{stop-color:#7cd6fd}.stop-color.purple{stop-color:#b554ff}.stop-color.magenta{stop-color:#ffa3ef}',void 0);var a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},s=(function(){function t(t){this.value=t}function e(e){function i(s,n){try{var r=e[s](n),o=r.value;o instanceof t?Promise.resolve(o.value).then(function(t){i("next",t)},function(t){i("throw",t)}):a(r.done?"return":"normal",r.value)}catch(t){a("throw",t)}}function a(t,e){switch(t){case"return":s.resolve({value:e,done:!0});break;case"throw":s.reject(e);break;default:s.resolve({value:e,done:!1})}(s=s.next)?i(s.key,s.arg):n=null}var s,n;this._invoke=function(t,e){return new Promise(function(a,r){var o={key:t,arg:e,resolve:a,reject:r,next:null};n?n=n.next=o:(s=n=o,i(t,e))})},"function"!=typeof e.return&&(this.return=void 0)}"function"==typeof Symbol&&Symbol.asyncIterator&&(e.prototype[Symbol.asyncIterator]=function(){return this}),e.prototype.next=function(t){return this._invoke("next",t)},e.prototype.throw=function(t){return this._invoke("throw",t)},e.prototype.return=function(t){return this._invoke("return",t)}}(),function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}),n=function(){function t(t,e){for(var i=0;i3&&void 0!==arguments[3]?arguments[3]:"linear",s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:void 0,n=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{},r={ease:"0.25 0.1 0.25 1",linear:"0 0 1 1",easein:"0.1 0.8 0.2 1",easeout:"0 0 0.58 1",easeinout:"0.42 0 0.58 1"},o=t.cloneNode(!0),l=t.cloneNode(!0);for(var h in e){var _=void 0;_="transform"===h?document.createElementNS("http://www.w3.org/2000/svg","animateTransform"):document.createElementNS("http://www.w3.org/2000/svg","animate");var u=n[h]||t.getAttribute(h),c=e[h],p={attributeName:h,from:u,to:c,begin:"0s",dur:i/1e3+"s",values:u+";"+c,keySplines:r[a],keyTimes:"0;1",calcMode:"spline",fill:"freeze"};s&&(p.type=s);for(var d in p)_.setAttribute(d,p[d]);o.appendChild(_),s?l.setAttribute(h,"translate("+c+")"):l.setAttribute(h,c)}return[o,l]},t.offset=function(t){var e=t.getBoundingClientRect();return{top:e.top+(document.documentElement.scrollTop||document.body.scrollTop),left:e.left+(document.documentElement.scrollLeft||document.body.scrollLeft)}},t.isElementInViewport=function(t){var e=t.getBoundingClientRect();return e.top>=0&&e.left>=0&&e.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&e.right<=(window.innerWidth||document.documentElement.clientWidth)},t.bind=function(t,e){if(t)for(var i in e){var a=e[i];i.split(/\s+/).forEach(function(e){t.addEventListener(e,a)})}},t.unbind=function(t,e){if(t)for(var i in e){var a=e[i];i.split(/\s+/).forEach(function(e){t.removeEventListener(e,a)})}},t.fire=function(t,e,i){var a=document.createEvent("HTMLEvents");a.initEvent(e,!0,!0);for(var s in i)a[s]=i[s];return t.dispatchEvent(a)};var u=function(){function e(t){var i=t.parent,a=void 0===i?null:i;s(this,e),this.parent=a,this.title_name="",this.title_value="",this.list_values=[],this.title_value_first=0,this.x=0,this.y=0,this.top=0,this.left=0,this.setup()}return n(e,[{key:"setup",value:function(){this.make_tooltip()}},{key:"refresh",value:function(){this.fill(),this.calc_position()}},{key:"make_tooltip",value:function(){var e=this;this.container=t.create("div",{inside:this.parent,className:"graph-svg-tip comparison",innerHTML:'\n\t\t\t\t
    \n\t\t\t\t
    '}),this.hide_tip(),this.title=this.container.querySelector(".title"),this.data_point_list=this.container.querySelector(".data-point-list"),this.parent.addEventListener("mouseleave",function(){e.hide_tip()})}},{key:"fill",value:function(){var e=this,i=void 0;i=this.title_value_first?""+this.title_value+""+this.title_name:this.title_name+""+this.title_value+"",this.title.innerHTML=i,this.data_point_list.innerHTML="",this.list_values.map(function(i){var a=t.create("li",{className:"border-top "+(i.color||"black"),innerHTML:''+(i.value?i.value:"")+"\n\t\t\t\t\t"+(i.title?i.title:"")});e.data_point_list.appendChild(a)})}},{key:"calc_position",value:function(){this.top=this.y-this.container.offsetHeight,this.left=this.x-this.container.offsetWidth/2;var t=this.parent.offsetWidth-this.container.offsetWidth,e=this.container.querySelector(".svg-pointer");if(this.left<0)e.style.left="calc(50% - "+-1*this.left+"px)",this.left=0;else if(this.left>t){var i=this.left-t;e.style.left="calc(50% + "+i+"px)",this.left=t}else e.style.left="50%"}},{key:"set_values",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"",s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:[],n=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0;this.title_name=i,this.title_value=a,this.list_values=s,this.x=t,this.y=e,this.title_value_first=n,this.refresh()}},{key:"hide_tip",value:function(){this.container.style.top="0px",this.container.style.left="0px",this.container.style.opacity="0"}},{key:"show_tip",value:function(){this.container.style.top=this.top+"px",this.container.style.left=this.left+"px",this.container.style.opacity="1"}}]),e}(),c=function(){function e(t){var i=t.parent,a=void 0===i?"":i,n=t.height,r=void 0===n?240:n,o=t.title,l=void 0===o?"":o,h=t.subtitle,_=void 0===h?"":h,u=t.data,c=void 0===u?{}:u,p=t.format_lambdas,d=void 0===p?{}:p,f=t.summary,g=void 0===f?[]:f,v=t.is_navigable,y=void 0===v?0:v,m=t.has_legend,x=void 0===m?0:m;s(this,e),this.raw_chart_args=arguments[0],this.parent=document.querySelector(a),this.title=l,this.subtitle=_,this.data=c,this.format_lambdas=d,this.specific_values=c.specific_values||[],this.summary=g,this.is_navigable=y,this.is_navigable&&(this.current_index=0),this.has_legend=x,this.chart_types=["line","scatter","bar","percentage","heatmap"],this.set_margins(r)}return n(e,[{key:"get_different_chart",value:function(t){if(this.chart_types.includes(t)||console.error("'"+t+"' is not a valid chart type."),t!==this.type){return{bar:["line","scatter","percentage"],line:["scatter","bar","percentage"],scatter:["line","bar","percentage"],percentage:["bar","line","scatter"],heatmap:[]}[this.type].includes(t)||console.error("'"+this.type+"' chart cannot be converted to a '"+t+"' chart."),new m({parent:this.raw_chart_args.parent,title:this.title,data:this.raw_chart_args.data,type:t,height:this.raw_chart_args.height})}}},{key:"set_margins",value:function(t){this.base_height=t,this.height=t-40,this.translate_x=60,this.translate_y=10}},{key:"setup",value:function(){this.bind_window_events(),this.refresh(!0)}},{key:"bind_window_events",value:function(){var t=this;window.addEventListener("resize",function(){return t.refresh()}),window.addEventListener("orientationchange",function(){return t.refresh()})}},{key:"refresh",value:function(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.setup_base_values(),this.set_width(),this.setup_container(),this.setup_components(),this.setup_values(),this.setup_utils(),this.make_graph_components(t),this.make_tooltip(),this.summary.length>0?this.show_custom_summary():this.show_summary(),this.is_navigable&&this.setup_navigation(t)}},{key:"set_width",value:function(){var t=this,e=0;this.specific_values.map(function(i){t.get_strwidth(i.title)>e&&(e=t.get_strwidth(i.title)-40)}),this.base_width=this.parent.offsetWidth-e,this.width=this.base_width-2*this.translate_x}},{key:"setup_base_values",value:function(){}},{key:"setup_container",value:function(){this.container=t.create("div",{className:"chart-container",innerHTML:'
    '+this.title+'
    \n\t\t\t\t
    '+this.subtitle+'
    \n\t\t\t\t
    \n\t\t\t\t
    '}),this.parent.innerHTML="",this.parent.appendChild(this.container),this.chart_wrapper=this.container.querySelector(".frappe-chart"),this.stats_wrapper=this.container.querySelector(".graph-stats-container"),this.make_chart_area(),this.make_draw_area()}},{key:"make_chart_area",value:function(){return this.svg=t.createSVG("svg",{className:"chart",inside:this.chart_wrapper,width:this.base_width,height:this.base_height}),this.svg_defs=t.createSVG("defs",{inside:this.svg}),this.svg}},{key:"make_draw_area",value:function(){this.draw_area=t.createSVG("g",{className:this.type+"-chart",inside:this.svg,transform:"translate("+this.translate_x+", "+this.translate_y+")"})}},{key:"setup_components",value:function(){}},{key:"make_tooltip",value:function(){this.tip=new u({parent:this.chart_wrapper}),this.bind_tooltip()}},{key:"show_summary",value:function(){}},{key:"show_custom_summary",value:function(){var e=this;this.summary.map(function(i){var a=t.create("div",{className:"stats",innerHTML:''+i.title+": "+i.value+""});e.stats_wrapper.appendChild(a)})}},{key:"setup_navigation",value:function(){var e=this,i=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.make_overlay(),i&&(this.bind_overlay(),document.addEventListener("keydown",function(i){t.isElementInViewport(e.chart_wrapper)&&("37"==(i=i||window.event).keyCode?e.on_left_arrow():"39"==i.keyCode?e.on_right_arrow():"38"==i.keyCode?e.on_up_arrow():"40"==i.keyCode?e.on_down_arrow():"13"==i.keyCode&&e.on_enter_key())}))}},{key:"make_overlay",value:function(){}},{key:"bind_overlay",value:function(){}},{key:"bind_units",value:function(){}},{key:"on_left_arrow",value:function(){}},{key:"on_right_arrow",value:function(){}},{key:"on_up_arrow",value:function(){}},{key:"on_down_arrow",value:function(){}},{key:"on_enter_key",value:function(){}},{key:"get_data_point",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.current_index,e={index:t},i=this.y[0];return["svg_units","y_tops","values"].map(function(a){var s=a.slice(0,a.length-1);e[s]=i[a][t]}),e.label=this.x[t],e}},{key:"update_current_data_point",value:function(e){(e=parseInt(e))<0&&(e=0),e>=this.x.length&&(e=this.x.length-1),e!==this.current_index&&(this.current_index=e,t.fire(this.parent,"data-select",this.get_data_point()))}},{key:"get_strwidth",value:function(t){return 8*(t+"").length}},{key:"setup_utils",value:function(){}}]),e}(),p=function(a){function u(t){s(this,u);var e=l(this,(u.__proto__||Object.getPrototypeOf(u)).call(this,t));return e.x=e.data.labels,e.y=e.data.datasets,e.is_series=t.is_series,e.get_y_label=e.format_lambdas.y_label,e.get_y_tooltip=e.format_lambdas.y_tooltip,e.get_x_tooltip=e.format_lambdas.x_tooltip,e.colors=["green","blue","violet","red","orange","yellow","light-blue","light-green","purple","magenta"],e.zero_line=e.height,e}return o(u,c),n(u,[{key:"setup_values",value:function(){this.data.datasets.map(function(t){t.values=t.values.map(function(t){return isNaN(t)?0:t})}),this.setup_x(),this.setup_y()}},{key:"setup_x",value:function(){var t=this;this.set_avg_unit_width_and_x_offset(),this.x_axis_positions&&(this.x_old_axis_positions=this.x_axis_positions.slice()),this.x_axis_positions=this.x.map(function(i,a){return e(t.x_offset+a*t.avg_unit_width)}),this.x_old_axis_positions||(this.x_old_axis_positions=this.x_axis_positions.slice())}},{key:"setup_y",value:function(){this.y_axis_values&&(this.y_old_axis_values=this.y_axis_values.slice());var t=this.get_all_y_values();this.y_sums&&this.y_sums.length>0&&(t=t.concat(this.y_sums)),this.y_axis_values=this.get_y_axis_points(t),this.y_old_axis_values||(this.y_old_axis_values=this.y_axis_values.slice());var e=this.y_axis_values,i=e[e.length-1]-e[0];this.multiplier&&(this.old_multiplier=this.multiplier),this.multiplier=this.height/i,this.old_multiplier||(this.old_multiplier=this.multiplier);var a=e.indexOf(0),s=(e[1]-e[0])*this.multiplier;this.zero_line&&(this.old_zero_line=this.zero_line),this.zero_line=this.height-a*s,this.old_zero_line||(this.old_zero_line=this.zero_line)}},{key:"setup_components",value:function(){r(u.prototype.__proto__||Object.getPrototypeOf(u.prototype),"setup_components",this).call(this),this.setup_marker_components(),this.setup_aggregation_components(),this.setup_graph_components()}},{key:"setup_marker_components",value:function(){this.y_axis_group=t.createSVG("g",{className:"y axis",inside:this.draw_area}),this.x_axis_group=t.createSVG("g",{className:"x axis",inside:this.draw_area}),this.specific_y_group=t.createSVG("g",{className:"specific axis",inside:this.draw_area})}},{key:"setup_aggregation_components",value:function(){this.sum_group=t.createSVG("g",{className:"data-points",inside:this.draw_area}),this.average_group=t.createSVG("g",{className:"chart-area",inside:this.draw_area})}},{key:"setup_graph_components",value:function(){var e=this;this.svg_units_groups=[],this.y.map(function(i,a){e.svg_units_groups[a]=t.createSVG("g",{className:"data-points data-points-"+a,inside:e.draw_area})})}},{key:"make_graph_components",value:function(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.make_y_axis(),this.make_x_axis(),this.draw_graph(t),this.make_y_specifics()}},{key:"make_x_axis",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],i=void 0,a=void 0,s=void 0,n="";if("span"===this.x_axis_mode?(i=-7,a=this.height+15,s=this.height+25):"tick"===this.x_axis_mode&&(i=this.height,a=6,s=9,n="x-axis-label"),this.x_axis_group.setAttribute("transform","translate(0,"+i+")"),e)this.make_anim_x_axis(a,s,n);else{var r=1.5*this.avg_unit_width,o=r/8;this.x_axis_group.textContent="",this.x.map(function(e,i){var l=t.get_strwidth(e)+2;if(l>r)if(t.is_series){for(var h=1;l/h*2>r;)h++;if(i%h!=0)return}else e=e.slice(0,o-3)+" ...";t.x_axis_group.appendChild(t.make_x_line(a,s,e,"x-value-text",n,t.x_axis_positions[i]))})}}},{key:"make_y_axis",value:function(){var t=this;if(arguments.length>0&&void 0!==arguments[0]&&arguments[0])return this.make_anim_y_axis(),void this.make_anim_y_specifics();var e=this.get_y_axis_line_props(),i=h(e,4),a=i[0],s=i[1],n=i[2],r=i[3];this.y_axis_group.textContent="",this.y_axis_values.map(function(e,i){t.y_axis_group.appendChild(t.make_y_line(r,a,s,e,"y-value-text",n,t.zero_line-e*t.multiplier,0===e&&0!==i))})}},{key:"get_y_axis_line_props",value:function(){if(arguments.length>0&&void 0!==arguments[0]&&arguments[0])return[this.width,this.width+5,"specific-value",0];var t=void 0,e="",i=0;return"span"===this.y_axis_mode?(t=this.width+6,i=-6):"tick"===this.y_axis_mode&&(t=-6,e="y-axis-label"),[t,-9,e,i]}},{key:"draw_graph",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];!this.raw_chart_args.hasOwnProperty("init")||this.raw_chart_args.init?e?this.draw_new_graph_and_animate():this.y.map(function(e,i){e.svg_units=[],t.make_path&&t.make_path(e,i,t.x_axis_positions,e.y_tops,e.color||t.colors[i]),t.make_new_units(e,i)}):this.y.map(function(e,i){e.svg_units=[],t.make_path&&t.make_path(e,i,t.x_axis_positions,e.y_tops,e.color||t.colors[i]),t.make_new_units(e,i),t.calc_y_dependencies()})}},{key:"draw_new_graph_and_animate",value:function(){var t=this,e=[];this.y.map(function(i,a){i.y_tops=new Array(i.values.length).fill(t.zero_line),e.push({values:i.values}),i.svg_units=[],t.make_path&&t.make_path(i,a,t.x_axis_positions,i.y_tops,i.color||t.colors[a]),t.make_new_units(i,a)}),setTimeout(function(){t.update_values(e)},350)}},{key:"setup_navigation",value:function(t){var e=this;t?setTimeout(function(){r(u.prototype.__proto__||Object.getPrototypeOf(u.prototype),"setup_navigation",e).call(e,t)},500):r(u.prototype.__proto__||Object.getPrototypeOf(u.prototype),"setup_navigation",this).call(this,t)}},{key:"make_new_units",value:function(t,e){this.make_new_units_for_dataset(this.x_axis_positions,t.y_tops,t.color||this.colors[e],e,this.y.length)}},{key:"make_new_units_for_dataset",value:function(t,e,i,a,s,n,r,o){var l=this;n||(n=this.svg_units_groups[a]),r||(r=this.y[a].svg_units),o||(o=this.unit_args),n.textContent="",r.length=0,e.map(function(e,h){var _=l.draw[o.type](t[h],e,o.args,i,h,a,s);n.appendChild(_),r.push(_)}),this.is_navigable&&this.bind_units(r)}},{key:"make_y_specifics",value:function(){var t=this;this.specific_y_group.textContent="",this.specific_values.map(function(e){t.specific_y_group.appendChild(t.make_y_line(0,t.width,t.width+5,e.title.toUpperCase(),"specific-value","specific-value",t.zero_line-e.value*t.multiplier,!1,e.line_type))})}},{key:"bind_tooltip",value:function(){var e=this;this.chart_wrapper.addEventListener("mousemove",function(i){var a=t.offset(e.chart_wrapper),s=i.pageX-a.left-e.translate_x;i.pageY-a.top-e.translate_y=0;i--){var a=this.x_axis_positions[i];if(t>a-this.avg_unit_width/2){var s=a+this.translate_x,n=this.y_min_tops[i]+this.translate_y,r=this.x.formatted&&this.x.formatted.length>0?this.x.formatted[i]:this.x[i],o=this.y.map(function(t,a){return{title:t.title,value:t.formatted?t.formatted[i]:t.values[i],color:t.color||e.colors[a]}});this.tip.set_values(s,n,r,"",o),this.tip.show_tip();break}}}},{key:"show_sums",value:function(){var t=this;this.updating=!0,this.y_sums=new Array(this.x_axis_positions.length).fill(0),this.y.map(function(e){e.values.map(function(e,i){t.y_sums[i]+=e})}),this.update_values(),this.sum_units=[],this.make_new_units_for_dataset(this.x_axis_positions,this.y_sums.map(function(i){return e(t.zero_line-i*t.multiplier)}),"light-grey",0,1,this.sum_group,this.sum_units),this.updating=!1}},{key:"hide_sums",value:function(){this.updating||(this.y_sums=[],this.sum_group.textContent="",this.sum_units=[],this.update_values())}},{key:"show_averages",value:function(){var t=this;this.old_specific_values=this.specific_values.slice(),this.y.map(function(e,i){var a=0;e.values.map(function(t){a+=t});var s=a/e.values.length;t.specific_values.push({title:"AVG "+(i+1),line_type:"dashed",value:s,auto:1})}),this.update_values()}},{key:"hide_averages",value:function(){var t=this;this.old_specific_values=this.specific_values.slice();var e=[];this.specific_values.map(function(t,i){t.auto&&e.unshift(i)}),e.map(function(e){t.specific_values.splice(e,1)}),this.update_values()}},{key:"update_values",value:function(t,e){var a=this;e||(e=this.x),this.elements_to_animate=[],this.updating=!0,this.old_x_values=this.x.slice(),this.old_y_axis_tops=this.y.map(function(t){return t.y_tops.slice()}),this.old_y_values=this.y.map(function(t){return t.values}),this.no_of_extra_pts=e.length-this.x.length,t&&this.y.map(function(e,i){e.values=t[i].values}),e&&(this.x=e),this.setup_x(),this.setup_y(),i(this.x_old_axis_positions,this.x_axis_positions)||(this.make_x_axis(!0),setTimeout(function(){a.updating||a.make_x_axis()},350)),(!i(this.y_old_axis_values,this.y_axis_values)||this.old_specific_values&&!i(this.old_specific_values,this.specific_values))&&(this.make_y_axis(!0),setTimeout(function(){a.updating||(a.make_y_axis(),a.make_y_specifics())},350)),this.calc_y_dependencies(),this.animate_graphs(),this.run_animation(),this.updating=!1}},{key:"add_data_point",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.x.length,a=this.y.map(function(t){return{values:t.values}});a.map(function(e,a){e.values.splice(i,0,t[a])});var s=this.x.slice();s.splice(i,0,e),this.update_values(a,s)}},{key:"remove_data_point",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.x.length-1;if(!(this.x.length<3)){var e=this.y.map(function(t){return{values:t.values}});e.map(function(e){e.values.splice(t,1)});var i=this.x.slice();i.splice(t,1),this.update_values(e,i)}}},{key:"run_animation",value:function(){var e=this,i=t.runSVGAnimation(this.svg,this.elements_to_animate);this.svg.parentNode==this.chart_wrapper&&(this.chart_wrapper.removeChild(this.svg),this.chart_wrapper.appendChild(i)),setTimeout(function(){i.parentNode==e.chart_wrapper&&(e.chart_wrapper.removeChild(i),e.chart_wrapper.appendChild(e.svg))},250)}},{key:"animate_graphs",value:function(){var t=this;this.y.map(function(e,i){var a=t.calc_old_and_new_postions(e,i),s=h(a,4),n=s[0],r=s[1],o=s[2],l=s[3];t.no_of_extra_pts>=0&&(t.make_path&&t.make_path(e,i,n,r,e.color||t.colors[i]),t.make_new_units_for_dataset(n,r,e.color||t.colors[i],i,t.y.length)),e.path&&t.animate_path(e,i,n,r,o,l),t.animate_units(e,i,n,r,o,l)}),setTimeout(function(){t.y.map(function(e,i){t.make_path&&t.make_path(e,i,t.x_axis_positions,e.y_tops,e.color||t.colors[i]),t.make_new_units(e,i)})},400)}},{key:"animate_path",value:function(t,e,i,a,s,n){var r=n.map(function(t,e){return s[e]+","+t}).join("L"),o=[{unit:t.path,object:t,key:"path"},{d:"M"+r},350,"easein"];if(this.elements_to_animate.push(o),t.region_path){var l="0,"+this.zero_line+"L",h="L"+this.width+","+this.zero_line,_=[{unit:t.region_path,object:t,key:"region_path"},{d:"M"+l+r+h},350,"easein"];this.elements_to_animate.push(_)}}},{key:"animate_units",value:function(t,e,i,a,s,n){var r=this,o=this.unit_args.type;t.svg_units.map(function(i,a){void 0!==s[a]&&void 0!==n[a]&&r.elements_to_animate.push(r.animate[o]({unit:i,array:t.svg_units,index:a},s[a],n[a],e))})}},{key:"calc_old_and_new_postions",value:function(t,e){var i=this.x_old_axis_positions.slice(),a=this.x_axis_positions.slice(),s=this.old_y_axis_tops[e].slice(),n=t.y_tops.slice(),r=i[i.length-1],o=s[s.length-1],l=a[a.length-1],h=n[n.length-1];if(this.no_of_extra_pts>=0){var _=new Array(Math.abs(this.no_of_extra_pts)).fill(r),u=new Array(Math.abs(this.no_of_extra_pts)).fill(o);i=i.concat(_),s=s.concat(u)}else{var c=new Array(Math.abs(this.no_of_extra_pts)).fill(l),p=new Array(Math.abs(this.no_of_extra_pts)).fill(h);a=a.concat(c),n=n.concat(p)}return[i,s,a,n]}},{key:"make_anim_x_axis",value:function(t,e,i){var a=this,s=this.x_old_axis_positions,n=this.x_axis_positions,r=this.old_x_values,o=this.x,l=s[s.length-1];this.x_axis_group.textContent="",this.make_new_axis_anim_lines(s,n,r,o,l,function(s,n,r){"string"==typeof r&&(r=parseInt(r.substring(0,r.length-1)));var o=a.make_x_line(t,e,s,"x-value-text",i,n);a.x_axis_group.appendChild(o),a.elements_to_animate&&a.elements_to_animate.push([{unit:o,array:[0],index:0},{transform:r+", 0"},350,"easein","translate",{transform:n+", 0"}])})}},{key:"make_anim_y_axis",value:function(){var t=this,e=this.y_old_axis_values.map(function(e){return t.zero_line-e*t.multiplier}),i=this.y_axis_values.map(function(e){return t.zero_line-e*t.multiplier}),a=this.y_old_axis_values,s=this.y_axis_values,n=e[e.length-1];this.y_axis_group.textContent="",this.make_new_axis_anim_lines(e,i,a,s,n,this.add_and_animate_y_line.bind(this),this.y_axis_group)}},{key:"make_anim_y_specifics",value:function(){var t=this;this.specific_y_group.textContent="",this.specific_values.map(function(e){t.add_and_animate_y_line(e.title,t.old_zero_line-e.value*t.old_multiplier,t.zero_line-e.value*t.multiplier,0,t.specific_y_group,e.line_type,!0)})}},{key:"make_new_axis_anim_lines",value:function(t,e,i,a,s,n,r){var o=void 0,l=void 0,h=a.length-i.length;if(h>0)o=e.slice(0,t.length),l=a.slice(0,i.length);else{var _=new Array(Math.abs(h)).fill("");l=a.concat(_);var u=new Array(Math.abs(h)).fill(s+"F");o=e.concat(u)}if(l.map(function(e,i){n(e,t[i],o[i],i,r)}),h>0){var c=a.slice(i.length),p=e.slice(t.length);c.map(function(t,e){n(t,s,p[e],e,r)})}}},{key:"make_x_line",value:function(e,i,a,s,n,r){var o=t.createSVG("line",{x1:0,x2:0,y1:0,y2:e}),l=t.createSVG("text",{className:s,x:0,y:i,dy:".71em",innerHTML:a}),h=t.createSVG("g",{className:"tick "+n,transform:"translate("+r+", 0)"});return h.appendChild(o),h.appendChild(l),h}},{key:"make_y_line",value:function(e,i,a,s,n,r,o){var l=arguments.length>7&&void 0!==arguments[7]&&arguments[7],h=arguments.length>8&&void 0!==arguments[8]?arguments[8]:"",_=t.createSVG("line",{className:"dashed"===h?"dashed":"",x1:e,x2:i,y1:0,y2:0}),u=t.createSVG("text",{className:n,x:a,y:0,dy:".32em",innerHTML:s+""}),c=t.createSVG("g",{className:"tick "+r,transform:"translate(0, "+o+")","stroke-opacity":1});return l&&(_.style.stroke="rgba(27, 31, 35, 0.6)"),c.appendChild(_),c.appendChild(u),c}},{key:"add_and_animate_y_line",value:function(t,e,i,a,s,n){var r=arguments.length>6&&void 0!==arguments[6]&&arguments[6],o=!1;"string"==typeof i&&(i=parseInt(i.substring(0,i.length-1)),o=!0);var l={transform:"0, "+i},_={transform:"0, "+e};o&&(l["stroke-opacity"]=0);var u=this.get_y_axis_line_props(r),c=h(u,4),p=c[0],d=c[1],f=c[2],g=c[3],v=r?"specific-value":"y-value-text";t=r?(t+"").toUpperCase():t;var y=this.make_y_line(g,p,d,t,v,f,e,0===t&&0!==a,n);s.appendChild(y),this.elements_to_animate&&this.elements_to_animate.push([{unit:y,array:[0],index:0},l,350,"easein","translate",_])}},{key:"get_y_axis_points",value:function(t){var e=this,i=void 0,a=void 0,s=void 0,n=void 0,r=parseInt(Math.max.apply(Math,_(t))),o=parseInt(Math.min.apply(Math,_(t)));o>=0&&(o=0);var l=function(t,i){var a=void 0,s=void 0,n=void 0,r=void 0,o=void 0;if((t+"").length<=1)a=10,n=5;else{var l=e.calc_upper_bound_and_no_of_parts(t),_=h(l,2);a=_[0],n=_[1]}return o=a/n,r=e.calc_no_of_parts(i,o),s=r*o,[a,s,n,r,o]},u=-1*o;if(u<=r){var c=l(r,u),p=h(c,5);i=p[1],a=p[2],s=p[3],n=p[4],0===u&&(i=0,s=0)}else{var d=l(u,r),f=h(d,5);i=f[0],s=f[2],a=f[3],n=f[4]}a%2!=0&&s>0&&a++,s%2!=0&&(s++,i+=n);var g=a+s;return g>5&&(g/=2,n*=2,a/=2),r<(a-1)*n&&g--,this.get_intervals(-1*i,n,g)}},{key:"get_intervals",value:function(t,e,i){for(var a=[],s=0;s<=i;s++)a.push(t),t+=e;return a}},{key:"calc_upper_bound_and_no_of_parts",value:function(t){var e=Math.pow(10,(t+"").length-1),i=this.calc_no_of_parts(t,e);return[e*i,i]}},{key:"calc_no_of_parts",value:function(t,e){var i=Math.ceil(t/e);return i%2!=0&&i++,i}},{key:"get_optimal_no_of_parts",value:function(t){return t<5?t:t/2}},{key:"set_avg_unit_width_and_x_offset",value:function(){this.avg_unit_width=this.width/(this.x.length-1),this.x_offset=0}},{key:"get_all_y_values",value:function(){var t=[];return this.y.map(function(e){t=t.concat(e.values)}),t.concat(this.specific_values.map(function(t){return t.value}))}},{key:"calc_y_dependencies",value:function(){var t=this;this.y_min_tops=new Array(this.x_axis_positions.length).fill(9999),this.y.map(function(i){i.y_tops=i.values.map(function(i){return e(t.zero_line-i*t.multiplier)}),i.y_tops.map(function(e,i){e1&&void 0!==arguments[1]&&arguments[1],a="path-fill-gradient-"+e,s=t.createSVG("linearGradient",{inside:this.svg_defs,id:a,x1:0,x2:0,y1:0,y2:1}),n=function(e,i,a,s){t.createSVG("stop",{className:"stop-color "+a,inside:e,offset:i,"stop-opacity":s})},r=[1,.6,.2];return i&&(r=[.4,.2,0]),n(s,"0%",e,r[0]),n(s,"50%",e,r[1]),n(s,"100%",e,r[2]),a}}]),i}(),g=function(t){function e(t){s(this,e);var i=l(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,t));return i.type="scatter",t.dot_radius?i.dot_radius=t.dot_radius:i.dot_radius=8,i.setup(),i}return o(e,f),n(e,[{key:"setup_graph_components",value:function(){this.setup_path_groups(),r(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"setup_graph_components",this).call(this)}},{key:"setup_path_groups",value:function(){}},{key:"setup_values",value:function(){r(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"setup_values",this).call(this),this.unit_args={type:"dot",args:{radius:this.dot_radius}}}},{key:"make_paths",value:function(){}},{key:"make_path",value:function(){}}]),e}(),v=function(e){function i(t){s(this,i);var e=l(this,(i.__proto__||Object.getPrototypeOf(i)).call(this,t));return e.type="percentage",e.get_y_label=e.format_lambdas.y_label,e.get_x_tooltip=e.format_lambdas.x_tooltip,e.get_y_tooltip=e.format_lambdas.y_tooltip,e.max_slices=10,e.max_legend_points=6,e.colors=t.colors,(!e.colors||e.colors.length0}),i=e;if(e.length>this.max_slices){e.sort(function(t,e){return e[0]-t[0]}),i=e.slice(0,this.max_slices-1);var a=0;e.slice(this.max_slices-1).map(function(t){a+=t[0]}),i.push([a,"Rest"]),this.colors[this.max_slices-1]="grey"}this.labels=[],i.map(function(e){t.slice_totals.push(e[0]),t.labels.push(e[1])}),this.legend_totals=this.slice_totals.slice(0,this.max_legend_points)}},{key:"setup_utils",value:function(){}},{key:"make_graph_components",value:function(){var e=this;this.grand_total=this.slice_totals.reduce(function(t,e){return t+e},0),this.slices=[],this.slice_totals.map(function(i,a){var s=t.create("div",{className:"progress-bar background "+e.colors[a],inside:e.percentage_bar,styles:{width:100*i/e.grand_total+"%"}});e.slices.push(s)})}},{key:"bind_tooltip",value:function(){var e=this;this.slices.map(function(i,a){i.addEventListener("mouseenter",function(){var s=t.offset(e.chart_wrapper),n=t.offset(i),r=n.left-s.left+i.offsetWidth/2,o=n.top-s.top-6,l=(e.formatted_labels&&e.formatted_labels.length>0?e.formatted_labels[a]:e.labels[a])+": ",h=(100*e.slice_totals[a]/e.grand_total).toFixed(1);e.tip.set_values(r,o,l,h+"%"),e.tip.show_tip()})})}},{key:"show_summary",value:function(){var e=this,i=this.formatted_labels&&this.formatted_labels.length>0?this.formatted_labels:this.labels;this.legend_totals.map(function(a,s){a&&(t.create("div",{className:"stats",inside:e.stats_wrapper}).innerHTML='\n\t\t\t\t\t'+i[s]+":\n\t\t\t\t\t"+a+"\n\t\t\t\t")})}}]),i}(),y=function(e){function i(t){var e=t.start,a=void 0===e?"":e,n=t.domain,r=void 0===n?"":n,o=t.subdomain,h=void 0===o?"":o,_=t.data,u=void 0===_?{}:_,c=t.discrete_domains,p=void 0===c?0:c,d=t.count_label,f=void 0===d?"":d;s(this,i);var g=l(this,(i.__proto__||Object.getPrototypeOf(i)).call(this,arguments[0]));g.type="heatmap",g.domain=r,g.subdomain=h,g.data=u,g.discrete_domains=p,g.count_label=f;var v=new Date;return g.start=a||g.add_days(v,365),g.legend_colors=["#ebedf0","#c6e48b","#7bc96f","#239a3b","#196127"],g.translate_x=0,g.setup(),g}return o(i,c),n(i,[{key:"setup_base_values",value:function(){this.today=new Date,this.start||(this.start=new Date,this.start.setFullYear(this.start.getFullYear()-1)),this.first_week_start=new Date(this.start.toDateString()),this.last_week_start=new Date(this.today.toDateString()),7!==this.first_week_start.getDay()&&this.add_days(this.first_week_start,-1*this.first_week_start.getDay()),7!==this.last_week_start.getDay()&&this.add_days(this.last_week_start,-1*this.last_week_start.getDay()),this.no_of_cols=this.get_weeks_between(this.first_week_start+"",this.last_week_start+"")+1}},{key:"set_width",value:function(){this.base_width=12*this.no_of_cols,this.discrete_domains&&(this.base_width+=144)}},{key:"setup_components",value:function(){this.domain_label_group=t.createSVG("g",{className:"domain-label-group chart-label",inside:this.draw_area}),this.data_groups=t.createSVG("g",{className:"data-groups",inside:this.draw_area,transform:"translate(0, 20)"})}},{key:"setup_values",value:function(){this.domain_label_group.textContent="",this.data_groups.textContent="",this.distribution=this.get_distribution(this.data,this.legend_colors),this.month_names=["January","February","March","April","May","June","July","August","September","October","November","December"],this.render_all_weeks_and_store_x_values(this.no_of_cols)}},{key:"render_all_weeks_and_store_x_values",value:function(t){var e=new Date(this.first_week_start);this.week_col=0,this.current_month=e.getMonth(),this.months=[this.current_month+""],this.month_weeks={},this.month_start_points=[],this.month_weeks[this.current_month]=0,this.month_start_points.push(13);for(var i=0;i0&&void 0!==arguments[0]?arguments[0]:{},e=arguments[1],i=Object.keys(t).map(function(e){return t[e]}),a=Math.max.apply(Math,_(i)),s=1/(e.length-1),n=[];return e.map(function(t,e){var i=a*(s*e);n.push(i)}),n}},{key:"get_max_checkpoint",value:function(t,e){return e.filter(function(i,a){return 1===a?e[0]9?"":"0")+e,(i>9?"":"0")+i,t.getFullYear()].join("-")}},{key:"get_weeks_between",value:function(t,e){return Math.ceil(this.get_days_between(t,e)/7)}},{key:"get_days_between",value:function(t,e){return(this.treat_as_utc(e)-this.treat_as_utc(t))/864e5}},{key:"add_days",value:function(t,e){t.setDate(t.getDate()+e)}},{key:"get_month_name",value:function(){}}]),i}(),m=function t(e){return s(this,t),"line"===e.type?new f(arguments[0]):"bar"===e.type?new d(arguments[0]):"scatter"===e.type?new g(arguments[0]):"percentage"===e.type?new v(arguments[0]):"heatmap"===e.type?new y(arguments[0]):new f(arguments[0])};return m}(); //# sourceMappingURL=frappe-charts.min.js.map diff --git a/dist/frappe-charts.min.js.map b/dist/frappe-charts.min.js.map index f269a99..692fab0 100644 --- a/dist/frappe-charts.min.js.map +++ b/dist/frappe-charts.min.js.map @@ -1 +1 @@ -{"version":3,"file":"frappe-charts.min.js","sources":["../src/scripts/helpers/dom.js","../src/scripts/helpers/utils.js","../src/scripts/objects/SvgTip.js","../src/scripts/charts/BaseChart.js","../src/scripts/charts/AxisChart.js","../src/scripts/charts/BarChart.js","../src/scripts/charts/LineChart.js","../src/scripts/charts/ScatterChart.js","../src/scripts/charts/PercentageChart.js","../src/scripts/charts/Heatmap.js","../src/scripts/charts.js"],"sourcesContent":["export default function $(expr, con) {\n\treturn typeof expr === \"string\"? (con || document).querySelector(expr) : expr || null;\n}\n\n$.findNodeIndex = (node) =>\n{\n\tvar i = 0;\n\twhile (node.previousSibling) {\n\t\tnode = node.previousSibling;\n\t\ti++;\n\t}\n\treturn i;\n};\n\n$.create = (tag, o) => {\n\tvar element = document.createElement(tag);\n\n\tfor (var i in o) {\n\t\tvar val = o[i];\n\n\t\tif (i === \"inside\") {\n\t\t\t$(val).appendChild(element);\n\t\t}\n\t\telse if (i === \"around\") {\n\t\t\tvar ref = $(val);\n\t\t\tref.parentNode.insertBefore(element, ref);\n\t\t\telement.appendChild(ref);\n\t\t}\n\t\telse if (i in element) {\n\t\t\telement[i] = val;\n\t\t}\n\t\telse {\n\t\t\telement.setAttribute(i, val);\n\t\t}\n\t}\n\n\treturn element;\n};\n\n$.createSVG = (tag, o) => {\n\tvar element = document.createElementNS(\"http://www.w3.org/2000/svg\", tag);\n\n\tfor (var i in o) {\n\t\tvar val = o[i];\n\n\t\tif (i === \"inside\") {\n\t\t\t$(val).appendChild(element);\n\t\t}\n\t\telse if (i === \"around\") {\n\t\t\tvar ref = $(val);\n\t\t\tref.parentNode.insertBefore(element, ref);\n\t\t\telement.appendChild(ref);\n\t\t}\n\t\telse {\n\t\t\tif(i === \"className\") { i = \"class\"; }\n\t\t\tif(i === \"innerHTML\") {\n\t\t\t\telement['textContent'] = val;\n\t\t\t} else {\n\t\t\t\telement.setAttribute(i, val);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn element;\n};\n\n$.runSVGAnimation = (svg_container, elements) => {\n\t// let parent = elements[0][0]['unit'].parentNode;\n\n\tlet new_elements = [];\n\tlet anim_elements = [];\n\n\telements.map(element => {\n\t\tlet obj = element[0];\n\t\tlet parent = obj.unit.parentNode;\n\t\t// let index = let findNodeIndex(obj.unit);\n\n\t\tlet anim_element, new_element;\n\n\t\telement[0] = obj.unit;\n\n\t\t[anim_element, new_element] = $.animateSVG(...element);\n\n\t\tnew_elements.push(new_element);\n\t\tanim_elements.push([anim_element, parent]);\n\n\t\tparent.replaceChild(anim_element, obj.unit);\n\n\t\tif(obj.array) {\n\t\t\tobj.array[obj.index] = new_element;\n\t\t} else {\n\t\t\tobj.object[obj.key] = new_element;\n\t\t}\n\t});\n\n\tlet anim_svg = svg_container.cloneNode(true);\n\n\tanim_elements.map((anim_element, i) => {\n\t\tanim_element[1].replaceChild(new_elements[i], anim_element[0]);\n\t\telements[i][0] = new_elements[i];\n\t});\n\n\treturn anim_svg;\n};\n\n$.animateSVG = (element, props, dur, easing_type=\"linear\", type=undefined, old_values={}) => {\n\tlet easing = {\n\t\tease: \"0.25 0.1 0.25 1\",\n\t\tlinear: \"0 0 1 1\",\n\t\t// easein: \"0.42 0 1 1\",\n\t\teasein: \"0.1 0.8 0.2 1\",\n\t\teaseout: \"0 0 0.58 1\",\n\t\teaseinout: \"0.42 0 0.58 1\"\n\t};\n\n\tlet anim_element = element.cloneNode(true);\n\tlet new_element = element.cloneNode(true);\n\n\tfor(var attributeName in props) {\n\t\tlet animate_element;\n\t\tif(attributeName === 'transform') {\n\t\t\tanimate_element = document.createElementNS(\"http://www.w3.org/2000/svg\", \"animateTransform\");\n\t\t} else {\n\t\t\tanimate_element = document.createElementNS(\"http://www.w3.org/2000/svg\", \"animate\");\n\t\t}\n\t\tlet current_value = old_values[attributeName] || element.getAttribute(attributeName);\n\t\tlet value = props[attributeName];\n\n\t\tlet anim_attr = {\n\t\t\tattributeName: attributeName,\n\t\t\tfrom: current_value,\n\t\t\tto: value,\n\t\t\tbegin: \"0s\",\n\t\t\tdur: dur/1000 + \"s\",\n\t\t\tvalues: current_value + \";\" + value,\n\t\t\tkeySplines: easing[easing_type],\n\t\t\tkeyTimes: \"0;1\",\n\t\t\tcalcMode: \"spline\",\n\t\t\tfill: 'freeze'\n\t\t};\n\n\t\tif(type) {\n\t\t\tanim_attr[\"type\"] = type;\n\t\t}\n\n\t\tfor (var i in anim_attr) {\n\t\t\tanimate_element.setAttribute(i, anim_attr[i]);\n\t\t}\n\n\t\tanim_element.appendChild(animate_element);\n\n\t\tif(type) {\n\t\t\tnew_element.setAttribute(attributeName, `translate(${value})`);\n\t\t} else {\n\t\t\tnew_element.setAttribute(attributeName, value);\n\t\t}\n\t}\n\n\treturn [anim_element, new_element];\n};\n\n$.offset = (element) => {\n\tlet rect = element.getBoundingClientRect();\n\treturn {\n\t\t// https://stackoverflow.com/a/7436602/6495043\n\t\t// rect.top varies with scroll, so we add whatever has been\n\t\t// scrolled to it to get absolute distance from actual page top\n\t\ttop: rect.top + (document.documentElement.scrollTop || document.body.scrollTop),\n\t\tleft: rect.left + (document.documentElement.scrollLeft || document.body.scrollLeft)\n\t};\n};\n\n$.isElementInViewport = (el) => {\n\t// Although straightforward: https://stackoverflow.com/a/7557433/6495043\n\tvar rect = el.getBoundingClientRect();\n\n\treturn (\n\t\trect.top >= 0 &&\n rect.left >= 0 &&\n rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */\n rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */\n\t);\n};\n\n$.bind = (element, o) => {\n\tif (element) {\n\t\tfor (var event in o) {\n\t\t\tvar callback = o[event];\n\n\t\t\tevent.split(/\\s+/).forEach(function (event) {\n\t\t\t\telement.addEventListener(event, callback);\n\t\t\t});\n\t\t}\n\t}\n};\n\n$.unbind = (element, o) => {\n\tif (element) {\n\t\tfor (var event in o) {\n\t\t\tvar callback = o[event];\n\n\t\t\tevent.split(/\\s+/).forEach(function(event) {\n\t\t\t\telement.removeEventListener(event, callback);\n\t\t\t});\n\t\t}\n\t}\n};\n\n$.fire = (target, type, properties) => {\n\tvar evt = document.createEvent(\"HTMLEvents\");\n\n\tevt.initEvent(type, true, true );\n\n\tfor (var j in properties) {\n\t\tevt[j] = properties[j];\n\t}\n\n\treturn target.dispatchEvent(evt);\n};\n","export function float_2(d) {\n\treturn parseFloat(d.toFixed(2));\n}\n\nexport function arrays_equal(arr1, arr2) {\n\tif(arr1.length !== arr2.length) return false;\n\tlet are_equal = true;\n\tarr1.map((d, i) => {\n\t\tif(arr2[i] !== d) are_equal = false;\n\t});\n\treturn are_equal;\n}\n\nexport function shuffle(array) {\n\t// https://stackoverflow.com/a/2450976/6495043\n\t// Awesomeness: https://bost.ocks.org/mike/shuffle/\n\n\tvar currentIndex = array.length, temporaryValue, randomIndex;\n\n\t// While there remain elements to shuffle...\n\twhile (0 !== currentIndex) {\n\n\t\t// Pick a remaining element...\n\t\trandomIndex = Math.floor(Math.random() * currentIndex);\n\t\tcurrentIndex -= 1;\n\n\t\t// And swap it with the current element.\n\t\ttemporaryValue = array[currentIndex];\n\t\tarray[currentIndex] = array[randomIndex];\n\t\tarray[randomIndex] = temporaryValue;\n\t}\n\n\treturn array;\n}\n","import $ from '../helpers/dom';\n\nexport default class SvgTip {\n\tconstructor({\n\t\tparent = null\n\t}) {\n\t\tthis.parent = parent;\n\t\tthis.title_name = '';\n\t\tthis.title_value = '';\n\t\tthis.list_values = [];\n\t\tthis.title_value_first = 0;\n\n\t\tthis.x = 0;\n\t\tthis.y = 0;\n\n\t\tthis.top = 0;\n\t\tthis.left = 0;\n\n\t\tthis.setup();\n\t}\n\n\tsetup() {\n\t\tthis.make_tooltip();\n\t}\n\n\trefresh() {\n\t\tthis.fill();\n\t\tthis.calc_position();\n\t\t// this.show_tip();\n\t}\n\n\tmake_tooltip() {\n\t\tthis.container = $.create('div', {\n\t\t\tinside: this.parent,\n\t\t\tclassName: 'graph-svg-tip comparison',\n\t\t\tinnerHTML: `\n\t\t\t\t
      \n\t\t\t\t
      `\n\t\t});\n\t\tthis.hide_tip();\n\n\t\tthis.title = this.container.querySelector('.title');\n\t\tthis.data_point_list = this.container.querySelector('.data-point-list');\n\n\t\tthis.parent.addEventListener('mouseleave', () => {\n\t\t\tthis.hide_tip();\n\t\t});\n\t}\n\n\tfill() {\n\t\tlet title;\n\t\tif(this.title_value_first) {\n\t\t\ttitle = `${this.title_value}${this.title_name}`;\n\t\t} else {\n\t\t\ttitle = `${this.title_name}${this.title_value}`;\n\t\t}\n\t\tthis.title.innerHTML = title;\n\t\tthis.data_point_list.innerHTML = '';\n\n\t\tthis.list_values.map((set) => {\n\t\t\tlet li = $.create('li', {\n\t\t\t\tclassName: `border-top ${set.color || 'black'}`,\n\t\t\t\tinnerHTML: `${set.value ? set.value : '' }\n\t\t\t\t\t${set.title ? set.title : '' }`\n\t\t\t});\n\n\t\t\tthis.data_point_list.appendChild(li);\n\t\t});\n\t}\n\n\tcalc_position() {\n\t\tthis.top = this.y - this.container.offsetHeight;\n\t\tthis.left = this.x - this.container.offsetWidth/2;\n\t\tlet max_left = this.parent.offsetWidth - this.container.offsetWidth;\n\n\t\tlet pointer = this.container.querySelector('.svg-pointer');\n\n\t\tif(this.left < 0) {\n\t\t\tpointer.style.left = `calc(50% - ${-1 * this.left}px)`;\n\t\t\tthis.left = 0;\n\t\t} else if(this.left > max_left) {\n\t\t\tlet delta = this.left - max_left;\n\t\t\tpointer.style.left = `calc(50% + ${delta}px)`;\n\t\t\tthis.left = max_left;\n\t\t} else {\n\t\t\tpointer.style.left = `50%`;\n\t\t}\n\t}\n\n\tset_values(x, y, title_name = '', title_value = '', list_values = [], title_value_first = 0) {\n\t\tthis.title_name = title_name;\n\t\tthis.title_value = title_value;\n\t\tthis.list_values = list_values;\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\tthis.title_value_first = title_value_first;\n\t\tthis.refresh();\n\t}\n\n\thide_tip() {\n\t\tthis.container.style.top = '0px';\n\t\tthis.container.style.left = '0px';\n\t\tthis.container.style.opacity = '0';\n\t}\n\n\tshow_tip() {\n\t\tthis.container.style.top = this.top + 'px';\n\t\tthis.container.style.left = this.left + 'px';\n\t\tthis.container.style.opacity = '1';\n\t}\n}\n","import SvgTip from '../objects/SvgTip';\nimport $ from '../helpers/dom';\nimport Chart from '../charts';\n\nexport default class BaseChart {\n\tconstructor({\n\t\tparent = \"\",\n\t\theight = 240,\n\n\t\ttitle = '', subtitle = '',\n\n\t\tdata = {},\n\t\tformat_lambdas = {},\n\n\t\tsummary = [],\n\n\t\tis_navigable = 0,\n\t\thas_legend = 0,\n\n\t\ttype = '' // eslint-disable-line no-unused-vars\n\t}) {\n\t\tthis.raw_chart_args = arguments[0];\n\n\t\tthis.parent = document.querySelector(parent);\n\t\tthis.title = title;\n\t\tthis.subtitle = subtitle;\n\n\t\tthis.data = data;\n\t\tthis.format_lambdas = format_lambdas;\n\n\t\tthis.specific_values = data.specific_values || [];\n\t\tthis.summary = summary;\n\n\t\tthis.is_navigable = is_navigable;\n\t\tif(this.is_navigable) {\n\t\t\tthis.current_index = 0;\n\t\t}\n\t\tthis.has_legend = has_legend;\n\n\t\tthis.chart_types = ['line', 'scatter', 'bar', 'percentage', 'heatmap'];\n\n\t\tthis.set_margins(height);\n\t}\n\n\tget_different_chart(type) {\n\t\tif(!this.chart_types.includes(type)) {\n\t\t\tconsole.error(`'${type}' is not a valid chart type.`);\n\t\t}\n\t\tif(type === this.type) return;\n\n\t\t// Only across compatible types\n\t\tlet compatible_types = {\n\t\t\tbar: ['line', 'scatter', 'percentage'],\n\t\t\tline: ['scatter', 'bar', 'percentage'],\n\t\t\tscatter: ['line', 'bar', 'percentage'],\n\t\t\tpercentage: ['bar', 'line', 'scatter'],\n\t\t\theatmap: []\n\t\t};\n\n\t\tif(!compatible_types[this.type].includes(type)) {\n\t\t\tconsole.error(`'${this.type}' chart cannot be converted to a '${type}' chart.`);\n\t\t}\n\n\t\t// Okay, this is anticlimactic\n\t\t// this function will need to actually be 'change_chart_type(type)'\n\t\t// that will update only the required elements, but for now ...\n\t\treturn new Chart({\n\t\t\tparent: this.raw_chart_args.parent,\n\t\t\ttitle: this.title,\n\t\t\tdata: this.raw_chart_args.data,\n\t\t\ttype: type,\n\t\t\theight: this.raw_chart_args.height\n\t\t});\n\t}\n\n\tset_margins(height) {\n\t\tthis.base_height = height;\n\t\tthis.height = height - 40;\n\t\tthis.translate_x = 60;\n\t\tthis.translate_y = 10;\n\t}\n\n\tsetup() {\n\t\tthis.bind_window_events();\n\t\tthis.refresh(true);\n\t}\n\n\tbind_window_events() {\n\t\twindow.addEventListener('resize', () => this.refresh());\n\t\twindow.addEventListener('orientationchange', () => this.refresh());\n\t}\n\n\trefresh(init=false) {\n\t\tthis.setup_base_values();\n\t\tthis.set_width();\n\n\t\tthis.setup_container();\n\t\tthis.setup_components();\n\n\t\tthis.setup_values();\n\t\tthis.setup_utils();\n\n\t\tthis.make_graph_components(init);\n\t\tthis.make_tooltip();\n\n\t\tif(this.summary.length > 0) {\n\t\t\tthis.show_custom_summary();\n\t\t} else {\n\t\t\tthis.show_summary();\n\t\t}\n\n\t\tif(this.is_navigable) {\n\t\t\tthis.setup_navigation(init);\n\t\t}\n\t}\n\n\tset_width() {\n\t\tlet special_values_width = 0;\n\t\tthis.specific_values.map(val => {\n\t\t\tif(this.get_strwidth(val.title) > special_values_width) {\n\t\t\t\tspecial_values_width = this.get_strwidth(val.title) - 40;\n\t\t\t}\n\t\t});\n\t\tthis.base_width = this.parent.offsetWidth - special_values_width;\n\t\tthis.width = this.base_width - this.translate_x * 2;\n\t}\n\n\tsetup_base_values() {}\n\n\tsetup_container() {\n\t\tthis.container = $.create('div', {\n\t\t\tclassName: 'chart-container',\n\t\t\tinnerHTML: `
      ${this.title}
      \n\t\t\t\t
      ${this.subtitle}
      \n\t\t\t\t
      \n\t\t\t\t
      `\n\t\t});\n\n\t\t// Chart needs a dedicated parent element\n\t\tthis.parent.innerHTML = '';\n\t\tthis.parent.appendChild(this.container);\n\n\t\tthis.chart_wrapper = this.container.querySelector('.frappe-chart');\n\t\tthis.stats_wrapper = this.container.querySelector('.graph-stats-container');\n\n\t\tthis.make_chart_area();\n\t\tthis.make_draw_area();\n\t}\n\n\tmake_chart_area() {\n\t\tthis.svg = $.createSVG('svg', {\n\t\t\tclassName: 'chart',\n\t\t\tinside: this.chart_wrapper,\n\t\t\twidth: this.base_width,\n\t\t\theight: this.base_height\n\t\t});\n\n\t\tthis.svg_defs = $.createSVG('defs', {\n\t\t\tinside: this.svg,\n\t\t});\n\n\t\treturn this.svg;\n\t}\n\n\tmake_draw_area() {\n\t\tthis.draw_area = $.createSVG(\"g\", {\n\t\t\tclassName: this.type + '-chart',\n\t\t\tinside: this.svg,\n\t\t\ttransform: `translate(${this.translate_x}, ${this.translate_y})`\n\t\t});\n\t}\n\n\tsetup_components() { }\n\n\tmake_tooltip() {\n\t\tthis.tip = new SvgTip({\n\t\t\tparent: this.chart_wrapper,\n\t\t});\n\t\tthis.bind_tooltip();\n\t}\n\n\n\tshow_summary() {}\n\tshow_custom_summary() {\n\t\tthis.summary.map(d => {\n\t\t\tlet stats = $.create('div', {\n\t\t\t\tclassName: 'stats',\n\t\t\t\tinnerHTML: `${d.title}: ${d.value}`\n\t\t\t});\n\t\t\tthis.stats_wrapper.appendChild(stats);\n\t\t});\n\t}\n\n\tsetup_navigation(init=false) {\n\t\tthis.make_overlay();\n\n\t\tif(init) {\n\t\t\tthis.bind_overlay();\n\n\t\t\tdocument.addEventListener('keydown', (e) => {\n\t\t\t\tif($.isElementInViewport(this.chart_wrapper)) {\n\t\t\t\t\te = e || window.event;\n\n\t\t\t\t\tif (e.keyCode == '37') {\n\t\t\t\t\t\tthis.on_left_arrow();\n\t\t\t\t\t} else if (e.keyCode == '39') {\n\t\t\t\t\t\tthis.on_right_arrow();\n\t\t\t\t\t} else if (e.keyCode == '38') {\n\t\t\t\t\t\tthis.on_up_arrow();\n\t\t\t\t\t} else if (e.keyCode == '40') {\n\t\t\t\t\t\tthis.on_down_arrow();\n\t\t\t\t\t} else if (e.keyCode == '13') {\n\t\t\t\t\t\tthis.on_enter_key();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tmake_overlay() {}\n\tbind_overlay() {}\n\tbind_units() {}\n\n\ton_left_arrow() {}\n\ton_right_arrow() {}\n\ton_up_arrow() {}\n\ton_down_arrow() {}\n\ton_enter_key() {}\n\n\tget_data_point(index=this.current_index) {\n\t\t// check for length\n\t\tlet data_point = {\n\t\t\tindex: index\n\t\t};\n\t\tlet y = this.y[0];\n\t\t['svg_units', 'y_tops', 'values'].map(key => {\n\t\t\tlet data_key = key.slice(0, key.length-1);\n\t\t\tdata_point[data_key] = y[key][index];\n\t\t});\n\t\tdata_point.label = this.x[index];\n\t\treturn data_point;\n\t}\n\n\tupdate_current_data_point(index) {\n\t\tindex = parseInt(index);\n\t\tif(index < 0) index = 0;\n\t\tif(index >= this.x.length) index = this.x.length - 1;\n\t\tif(index === this.current_index) return;\n\t\tthis.current_index = index;\n\t\t$.fire(this.parent, \"data-select\", this.get_data_point());\n\t}\n\n\t// Helpers\n\tget_strwidth(string) {\n\t\treturn (string+\"\").length * 8;\n\t}\n\n\t// Objects\n\tsetup_utils() { }\n}\n","import $ from '../helpers/dom';\nimport { float_2, arrays_equal } from '../helpers/utils';\nimport BaseChart from './BaseChart';\n\nexport default class AxisChart extends BaseChart {\n\tconstructor(args) {\n\t\tsuper(args);\n\n\t\tthis.x = this.data.labels;\n\t\tthis.y = this.data.datasets;\n\n\t\tthis.is_series = args.is_series;\n\n\t\tthis.get_y_label = this.format_lambdas.y_label;\n\t\tthis.get_y_tooltip = this.format_lambdas.y_tooltip;\n\t\tthis.get_x_tooltip = this.format_lambdas.x_tooltip;\n\n\t\tthis.colors = ['green', 'blue', 'violet', 'red', 'orange',\n\t\t\t'yellow', 'light-blue', 'light-green', 'purple', 'magenta'];\n\n\t\tthis.zero_line = this.height;\n\t}\n\n\tsetup_values() {\n\t\tthis.data.datasets.map(d => {\n\t\t\td.values = d.values.map(val => (!isNaN(val) ? val : 0));\n\t\t});\n\t\tthis.setup_x();\n\t\tthis.setup_y();\n\t}\n\n\tsetup_x() {\n\t\tthis.set_avg_unit_width_and_x_offset();\n\n\t\tif(this.x_axis_positions) {\n\t\t\tthis.x_old_axis_positions = this.x_axis_positions.slice();\n\t\t}\n\t\tthis.x_axis_positions = this.x.map((d, i) =>\n\t\t\tfloat_2(this.x_offset + i * this.avg_unit_width));\n\n\t\tif(!this.x_old_axis_positions) {\n\t\t\tthis.x_old_axis_positions = this.x_axis_positions.slice();\n\t\t}\n\t}\n\n\tsetup_y() {\n\t\tif(this.y_axis_values) {\n\t\t\tthis.y_old_axis_values = this.y_axis_values.slice();\n\t\t}\n\n\t\tlet values = this.get_all_y_values();\n\n\t\tif(this.y_sums && this.y_sums.length > 0) {\n\t\t\tvalues = values.concat(this.y_sums);\n\t\t}\n\n\t\tthis.y_axis_values = this.get_y_axis_points(values);\n\n\t\tif(!this.y_old_axis_values) {\n\t\t\tthis.y_old_axis_values = this.y_axis_values.slice();\n\t\t}\n\n\t\tconst y_pts = this.y_axis_values;\n\t\tconst value_range = y_pts[y_pts.length-1] - y_pts[0];\n\n\t\tif(this.multiplier) this.old_multiplier = this.multiplier;\n\t\tthis.multiplier = this.height / value_range;\n\t\tif(!this.old_multiplier) this.old_multiplier = this.multiplier;\n\n\t\tconst zero_index = y_pts.indexOf(0);\n\t\tconst interval = y_pts[1] - y_pts[0];\n\t\tconst interval_height = interval * this.multiplier;\n\n\t\tif(this.zero_line) this.old_zero_line = this.zero_line;\n\t\tthis.zero_line = this.height - (zero_index * interval_height);\n\t\tif(!this.old_zero_line) this.old_zero_line = this.zero_line;\n\t}\n\n\tsetup_components() {\n\t\tsuper.setup_components();\n\t\tthis.setup_marker_components();\n\t\tthis.setup_aggregation_components();\n\t\tthis.setup_graph_components();\n\t}\n\n\tsetup_marker_components() {\n\t\tthis.y_axis_group = $.createSVG('g', {className: 'y axis', inside: this.draw_area});\n\t\tthis.x_axis_group = $.createSVG('g', {className: 'x axis', inside: this.draw_area});\n\t\tthis.specific_y_group = $.createSVG('g', {className: 'specific axis', inside: this.draw_area});\n\t}\n\n\tsetup_aggregation_components() {\n\t\tthis.sum_group = $.createSVG('g', {className: 'data-points', inside: this.draw_area});\n\t\tthis.average_group = $.createSVG('g', {className: 'chart-area', inside: this.draw_area});\n\t}\n\n\tsetup_graph_components() {\n\t\tthis.svg_units_groups = [];\n\t\tthis.y.map((d, i) => {\n\t\t\tthis.svg_units_groups[i] = $.createSVG('g', {\n\t\t\t\tclassName: 'data-points data-points-' + i,\n\t\t\t\tinside: this.draw_area\n\t\t\t});\n\t\t});\n\t}\n\n\tmake_graph_components(init=false) {\n\t\tthis.make_y_axis();\n\t\tthis.make_x_axis();\n\t\tthis.draw_graph(init);\n\t\tthis.make_y_specifics();\n\t}\n\n\t// make VERTICAL lines for x values\n\tmake_x_axis(animate=false) {\n\t\tlet start_at, height, text_start_at, axis_line_class = '';\n\t\tif(this.x_axis_mode === 'span') {\t\t// long spanning lines\n\t\t\tstart_at = -7;\n\t\t\theight = this.height + 15;\n\t\t\ttext_start_at = this.height + 25;\n\t\t} else if(this.x_axis_mode === 'tick'){\t// short label lines\n\t\t\tstart_at = this.height;\n\t\t\theight = 6;\n\t\t\ttext_start_at = 9;\n\t\t\taxis_line_class = 'x-axis-label';\n\t\t}\n\n\t\tthis.x_axis_group.setAttribute('transform', `translate(0,${start_at})`);\n\n\t\tif(animate) {\n\t\t\tthis.make_anim_x_axis(height, text_start_at, axis_line_class);\n\t\t\treturn;\n\t\t}\n\n\t\tlet allowed_space = this.avg_unit_width * 1.5;\n\t\tlet allowed_letters = allowed_space / 8;\n\n\t\tthis.x_axis_group.textContent = '';\n\t\tthis.x.map((point, i) => {\n\t\t\tlet space_taken = this.get_strwidth(point);\n\t\t\tif(space_taken > allowed_space) {\n\t\t\t\tif(this.is_series) {\n\t\t\t\t\t// Skip some axis lines if X axis is a series\n\t\t\t\t\tlet skips = 1;\n\t\t\t\t\twhile((space_taken/skips)*2 > allowed_space) {\n\t\t\t\t\t\tskips++;\n\t\t\t\t\t}\n\t\t\t\t\tif(i % skips !== 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tpoint = point.slice(0, allowed_letters-3) + \" ...\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.x_axis_group.appendChild(\n\t\t\t\tthis.make_x_line(\n\t\t\t\t\theight,\n\t\t\t\t\ttext_start_at,\n\t\t\t\t\tpoint,\n\t\t\t\t\t'x-value-text',\n\t\t\t\t\taxis_line_class,\n\t\t\t\t\tthis.x_axis_positions[i]\n\t\t\t\t)\n\t\t\t);\n\t\t});\n\t}\n\n\t// make HORIZONTAL lines for y values\n\tmake_y_axis(animate=false) {\n\t\tif(animate) {\n\t\t\tthis.make_anim_y_axis();\n\t\t\tthis.make_anim_y_specifics();\n\t\t\treturn;\n\t\t}\n\n\t\tlet [width, text_end_at, axis_line_class, start_at] = this.get_y_axis_line_props();\n\n\t\tthis.y_axis_group.textContent = '';\n\t\tthis.y_axis_values.map((value, i) => {\n\t\t\tthis.y_axis_group.appendChild(\n\t\t\t\tthis.make_y_line(\n\t\t\t\t\tstart_at,\n\t\t\t\t\twidth,\n\t\t\t\t\ttext_end_at,\n\t\t\t\t\tvalue,\n\t\t\t\t\t'y-value-text',\n\t\t\t\t\taxis_line_class,\n\t\t\t\t\tthis.zero_line - value * this.multiplier,\n\t\t\t\t\t(value === 0 && i !== 0) // Non-first Zero line\n\t\t\t\t)\n\t\t\t);\n\t\t});\n\t}\n\n\tget_y_axis_line_props(specific=false) {\n\t\tif(specific) {\n\t\t\treturn[this.width, this.width + 5, 'specific-value', 0];\n\t\t}\n\t\tlet width, text_end_at = -9, axis_line_class = '', start_at = 0;\n\t\tif(this.y_axis_mode === 'span') {\t\t// long spanning lines\n\t\t\twidth = this.width + 6;\n\t\t\tstart_at = -6;\n\t\t} else if(this.y_axis_mode === 'tick'){\t// short label lines\n\t\t\twidth = -6;\n\t\t\taxis_line_class = 'y-axis-label';\n\t\t}\n\n\t\treturn [width, text_end_at, axis_line_class, start_at];\n\t}\n\n\tdraw_graph(init=false) {\n\t\tif(this.raw_chart_args.hasOwnProperty(\"init\") && !this.raw_chart_args.init) {\n\t\t\tthis.y.map((d, i) => {\n\t\t\t\td.svg_units = [];\n\t\t\t\tthis.make_path && this.make_path(d, i, this.x_axis_positions, d.y_tops, d.color || this.colors[i]);\n\t\t\t\tthis.make_new_units(d, i);\n\t\t\t\tthis.calc_y_dependencies();\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif(init) {\n\t\t\tthis.draw_new_graph_and_animate();\n\t\t\treturn;\n\t\t}\n\t\tthis.y.map((d, i) => {\n\t\t\td.svg_units = [];\n\t\t\tthis.make_path && this.make_path(d, i, this.x_axis_positions, d.y_tops, d.color || this.colors[i]);\n\t\t\tthis.make_new_units(d, i);\n\t\t});\n\t}\n\n\tdraw_new_graph_and_animate() {\n\t\tlet data = [];\n\t\tthis.y.map((d, i) => {\n\t\t\t// Anim: Don't draw initial values, store them and update later\n\t\t\td.y_tops = new Array(d.values.length).fill(this.zero_line); // no value\n\t\t\tdata.push({values: d.values});\n\t\t\td.svg_units = [];\n\n\t\t\tthis.make_path && this.make_path(d, i, this.x_axis_positions, d.y_tops, d.color || this.colors[i]);\n\t\t\tthis.make_new_units(d, i);\n\t\t});\n\n\t\tsetTimeout(() => {\n\t\t\tthis.update_values(data);\n\t\t}, 350);\n\t}\n\n\tsetup_navigation(init) {\n\t\tif(init) {\n\t\t\t// Hack: defer nav till initial update_values\n\t\t\tsetTimeout(() => {\n\t\t\t\tsuper.setup_navigation(init);\n\t\t\t}, 500);\n\t\t} else {\n\t\t\tsuper.setup_navigation(init);\n\t\t}\n\t}\n\n\tmake_new_units(d, i) {\n\t\tthis.make_new_units_for_dataset(\n\t\t\tthis.x_axis_positions,\n\t\t\td.y_tops,\n\t\t\td.color || this.colors[i],\n\t\t\ti,\n\t\t\tthis.y.length\n\t\t);\n\t}\n\n\tmake_new_units_for_dataset(x_values, y_values, color, dataset_index,\n\t\tno_of_datasets, units_group, units_array, unit) {\n\n\t\tif(!units_group) units_group = this.svg_units_groups[dataset_index];\n\t\tif(!units_array) units_array = this.y[dataset_index].svg_units;\n\t\tif(!unit) unit = this.unit_args;\n\n\t\tunits_group.textContent = '';\n\t\tunits_array.length = 0;\n\n\t\ty_values.map((y, i) => {\n\t\t\tlet data_unit = this.draw[unit.type](\n\t\t\t\tx_values[i],\n\t\t\t\ty,\n\t\t\t\tunit.args,\n\t\t\t\tcolor,\n\t\t\t\ti,\n\t\t\t\tdataset_index,\n\t\t\t\tno_of_datasets\n\t\t\t);\n\t\t\tunits_group.appendChild(data_unit);\n\t\t\tunits_array.push(data_unit);\n\t\t});\n\n\t\tif(this.is_navigable) {\n\t\t\tthis.bind_units(units_array);\n\t\t}\n\t}\n\n\tmake_y_specifics() {\n\t\tthis.specific_y_group.textContent = '';\n\t\tthis.specific_values.map(d => {\n\t\t\tthis.specific_y_group.appendChild(\n\t\t\t\tthis.make_y_line(\n\t\t\t\t\t0,\n\t\t\t\t\tthis.width,\n\t\t\t\t\tthis.width + 5,\n\t\t\t\t\td.title.toUpperCase(),\n\t\t\t\t\t'specific-value',\n\t\t\t\t\t'specific-value',\n\t\t\t\t\tthis.zero_line - d.value * this.multiplier,\n\t\t\t\t\tfalse,\n\t\t\t\t\td.line_type\n\t\t\t\t)\n\t\t\t);\n\t\t});\n\t}\n\n\tbind_tooltip() {\n\t\t// TODO: could be in tooltip itself, as it is a given functionality for its parent\n\t\tthis.chart_wrapper.addEventListener('mousemove', (e) => {\n\t\t\tlet offset = $.offset(this.chart_wrapper);\n\t\t\tlet relX = e.pageX - offset.left - this.translate_x;\n\t\t\tlet relY = e.pageY - offset.top - this.translate_y;\n\n\t\t\tif(relY < this.height + this.translate_y * 2) {\n\t\t\t\tthis.map_tooltip_x_position_and_show(relX);\n\t\t\t} else {\n\t\t\t\tthis.tip.hide_tip();\n\t\t\t}\n\t\t});\n\t}\n\n\tmap_tooltip_x_position_and_show(relX) {\n\t\tif(!this.y_min_tops) return;\n\t\tfor(var i=this.x_axis_positions.length - 1; i >= 0 ; i--) {\n\t\t\tlet x_val = this.x_axis_positions[i];\n\t\t\t// let delta = i === 0 ? this.avg_unit_width : x_val - this.x_axis_positions[i-1];\n\t\t\tif(relX > x_val - this.avg_unit_width/2) {\n\t\t\t\tlet x = x_val + this.translate_x;\n\t\t\t\tlet y = this.y_min_tops[i] + this.translate_y;\n\n\t\t\t\tlet title = this.x.formatted && this.x.formatted.length>0\n\t\t\t\t\t? this.x.formatted[i] : this.x[i];\n\t\t\t\tlet values = this.y.map((set, j) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttitle: set.title,\n\t\t\t\t\t\tvalue: set.formatted ? set.formatted[i] : set.values[i],\n\t\t\t\t\t\tcolor: set.color || this.colors[j],\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\t\t// TODO: upside-down tooltips for negative values?\n\t\t\t\tthis.tip.set_values(x, y, title, '', values);\n\t\t\t\tthis.tip.show_tip();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// API\n\tshow_sums() {\n\t\tthis.updating = true;\n\n\t\tthis.y_sums = new Array(this.x_axis_positions.length).fill(0);\n\t\tthis.y.map(d => {\n\t\t\td.values.map( (value, i) => {\n\t\t\t\tthis.y_sums[i] += value;\n\t\t\t});\n\t\t});\n\n\t\t// Remake y axis, animate\n\t\tthis.update_values();\n\n\t\t// Then make sum units, don't animate\n\t\tthis.sum_units = [];\n\n\t\tthis.make_new_units_for_dataset(\n\t\t\tthis.x_axis_positions,\n\t\t\tthis.y_sums.map( val => float_2(this.zero_line - val * this.multiplier)),\n\t\t\t'light-grey',\n\t\t\t0,\n\t\t\t1,\n\t\t\tthis.sum_group,\n\t\t\tthis.sum_units\n\t\t);\n\n\t\t// this.make_path && this.make_path(d, i, old_x, old_y, d.color || this.colors[i]);\n\n\t\tthis.updating = false;\n\t}\n\n\thide_sums() {\n\t\tif(this.updating) return;\n\t\tthis.y_sums = [];\n\t\tthis.sum_group.textContent = '';\n\t\tthis.sum_units = [];\n\t\tthis.update_values();\n\t}\n\n\tshow_averages() {\n\t\tthis.old_specific_values = this.specific_values.slice();\n\t\tthis.y.map((d, i) => {\n\t\t\tlet sum = 0;\n\t\t\td.values.map(e => {sum+=e;});\n\t\t\tlet average = sum/d.values.length;\n\n\t\t\tthis.specific_values.push({\n\t\t\t\ttitle: \"AVG\" + \" \" + (i+1),\n\t\t\t\tline_type: \"dashed\",\n\t\t\t\tvalue: average,\n\t\t\t\tauto: 1\n\t\t\t});\n\t\t});\n\n\t\tthis.update_values();\n\t}\n\n\thide_averages() {\n\t\tthis.old_specific_values = this.specific_values.slice();\n\n\t\tlet indices_to_remove = [];\n\t\tthis.specific_values.map((d, i) => {\n\t\t\tif(d.auto) indices_to_remove.unshift(i);\n\t\t});\n\n\t\tindices_to_remove.map(index => {\n\t\t\tthis.specific_values.splice(index, 1);\n\t\t});\n\n\t\tthis.update_values();\n\t}\n\n\tupdate_values(new_y, new_x) {\n\t\tif(!new_x) {\n\t\t\tnew_x = this.x;\n\t\t}\n\t\tthis.elements_to_animate = [];\n\t\tthis.updating = true;\n\n\t\tthis.old_x_values = this.x.slice();\n\t\tthis.old_y_axis_tops = this.y.map(d => d.y_tops.slice());\n\n\t\tthis.old_y_values = this.y.map(d => d.values);\n\n\t\tthis.no_of_extra_pts = new_x.length - this.x.length;\n\n\t\t// Just update values prop, setup_x/y() will do the rest\n\t\tif(new_y) this.y.map((d, i) => {d.values = new_y[i].values;});\n\t\tif(new_x) this.x = new_x;\n\n\t\tthis.setup_x();\n\t\tthis.setup_y();\n\n\t\t// Animate only if positions have changed\n\t\tif(!arrays_equal(this.x_old_axis_positions, this.x_axis_positions)) {\n\t\t\tthis.make_x_axis(true);\n\t\t\tsetTimeout(() => {\n\t\t\t\tif(!this.updating) this.make_x_axis();\n\t\t\t}, 350);\n\t\t}\n\n\t\tif(!arrays_equal(this.y_old_axis_values, this.y_axis_values) ||\n\t\t\t(this.old_specific_values &&\n\t\t\t!arrays_equal(this.old_specific_values, this.specific_values))) {\n\n\t\t\tthis.make_y_axis(true);\n\t\t\tsetTimeout(() => {\n\t\t\t\tif(!this.updating) {\n\t\t\t\t\tthis.make_y_axis();\n\t\t\t\t\tthis.make_y_specifics();\n\t\t\t\t}\n\t\t\t}, 350);\n\t\t}\n\n\t\t// Change in data, so calculate dependencies\n\t\tthis.calc_y_dependencies();\n\n\t\tthis.animate_graphs();\n\n\t\t// Trigger animation with the animatable elements in this.elements_to_animate\n\t\tthis.run_animation();\n\n\t\tthis.updating = false;\n\t}\n\n\tadd_data_point(y_point, x_point, index=this.x.length) {\n\t\tlet new_y = this.y.map(data_set => { return {values:data_set.values}; });\n\t\tnew_y.map((d, i) => { d.values.splice(index, 0, y_point[i]); });\n\t\tlet new_x = this.x.slice();\n\t\tnew_x.splice(index, 0, x_point);\n\n\t\tthis.update_values(new_y, new_x);\n\t}\n\n\tremove_data_point(index = this.x.length-1) {\n\t\tif(this.x.length < 3) return;\n\n\t\tlet new_y = this.y.map(data_set => { return {values:data_set.values}; });\n\t\tnew_y.map((d) => { d.values.splice(index, 1); });\n\t\tlet new_x = this.x.slice();\n\t\tnew_x.splice(index, 1);\n\n\t\tthis.update_values(new_y, new_x);\n\t}\n\n\trun_animation() {\n\t\tlet anim_svg = $.runSVGAnimation(this.svg, this.elements_to_animate);\n\n\t\tif(this.svg.parentNode == this.chart_wrapper) {\n\t\t\tthis.chart_wrapper.removeChild(this.svg);\n\t\t\tthis.chart_wrapper.appendChild(anim_svg);\n\n\t\t}\n\n\t\t// Replace the new svg (data has long been replaced)\n\t\tsetTimeout(() => {\n\t\t\tif(anim_svg.parentNode == this.chart_wrapper) {\n\t\t\t\tthis.chart_wrapper.removeChild(anim_svg);\n\t\t\t\tthis.chart_wrapper.appendChild(this.svg);\n\t\t\t}\n\t\t}, 250);\n\t}\n\n\tanimate_graphs() {\n\t\tthis.y.map((d, i) => {\n\t\t\t// Pre-prep, equilize no of positions between old and new\n\t\t\tlet [old_x, old_y, new_x, new_y] = this.calc_old_and_new_postions(d, i);\n\t\t\tif(this.no_of_extra_pts >= 0) {\n\t\t\t\tthis.make_path && this.make_path(d, i, old_x, old_y, d.color || this.colors[i]);\n\t\t\t\tthis.make_new_units_for_dataset(old_x, old_y, d.color || this.colors[i], i, this.y.length);\n\t\t\t}\n\t\t\td.path && this.animate_path(d, i, old_x, old_y, new_x, new_y);\n\t\t\tthis.animate_units(d, i, old_x, old_y, new_x, new_y);\n\t\t});\n\n\t\t// TODO: replace with real units\n\t\tsetTimeout(() => {\n\t\t\tthis.y.map((d, i) => {\n\t\t\t\tthis.make_path && this.make_path(d, i, this.x_axis_positions, d.y_tops, d.color || this.colors[i]);\n\t\t\t\tthis.make_new_units(d, i);\n\t\t\t});\n\t\t}, 400);\n\t}\n\n\tanimate_path(d, i, old_x, old_y, new_x, new_y) {\n\t\t// Animate path\n\t\tconst new_points_list = new_y.map((y, i) => (new_x[i] + ',' + y));\n\t\tconst new_path_str = new_points_list.join(\"L\");\n\n\t\tconst path_args = [{unit: d.path, object: d, key: 'path'}, {d:\"M\"+new_path_str}, 350, \"easein\"];\n\t\tthis.elements_to_animate.push(path_args);\n\n\t\t// Animate region\n\t\tif(d.region_path) {\n\t\t\tlet reg_start_pt = `0,${this.zero_line}L`;\n\t\t\tlet reg_end_pt = `L${this.width},${this.zero_line}`;\n\n\t\t\tconst region_args = [\n\t\t\t\t{unit: d.region_path, object: d, key: 'region_path'},\n\t\t\t\t{d:\"M\" + reg_start_pt + new_path_str + reg_end_pt},\n\t\t\t\t350,\n\t\t\t\t\"easein\"\n\t\t\t];\n\t\t\tthis.elements_to_animate.push(region_args);\n\t\t}\n\t}\n\n\tanimate_units(d, index, old_x, old_y, new_x, new_y) {\n\t\tlet type = this.unit_args.type;\n\n\t\td.svg_units.map((unit, i) => {\n\t\t\tif(new_x[i] === undefined || new_y[i] === undefined) return;\n\t\t\tthis.elements_to_animate.push(this.animate[type](\n\t\t\t\t{unit:unit, array:d.svg_units, index: i}, // unit, with info to replace where it came from in the data\n\t\t\t\tnew_x[i],\n\t\t\t\tnew_y[i],\n\t\t\t\tindex\n\t\t\t));\n\t\t});\n\t}\n\n\tcalc_old_and_new_postions(d, i) {\n\t\tlet old_x = this.x_old_axis_positions.slice();\n\t\tlet new_x = this.x_axis_positions.slice();\n\n\t\tlet old_y = this.old_y_axis_tops[i].slice();\n\t\tlet new_y = d.y_tops.slice();\n\n\t\tconst last_old_x_pos = old_x[old_x.length - 1];\n\t\tconst last_old_y_pos = old_y[old_y.length - 1];\n\n\t\tconst last_new_x_pos = new_x[new_x.length - 1];\n\t\tconst last_new_y_pos = new_y[new_y.length - 1];\n\n\t\tif(this.no_of_extra_pts >= 0) {\n\t\t\t// First substitute current path with a squiggled one (looking the same but\n\t\t\t// having more points at end),\n\t\t\t// then animate to stretch it later to new points\n\t\t\t// (new points already have more points)\n\n\t\t\t// Hence, the extra end points will correspond to current(old) positions\n\t\t\tlet filler_x = new Array(Math.abs(this.no_of_extra_pts)).fill(last_old_x_pos);\n\t\t\tlet filler_y = new Array(Math.abs(this.no_of_extra_pts)).fill(last_old_y_pos);\n\n\t\t\told_x = old_x.concat(filler_x);\n\t\t\told_y = old_y.concat(filler_y);\n\n\t\t} else {\n\t\t\t// Just modify the new points to have extra points\n\t\t\t// with the same position at end\n\t\t\tlet filler_x = new Array(Math.abs(this.no_of_extra_pts)).fill(last_new_x_pos);\n\t\t\tlet filler_y = new Array(Math.abs(this.no_of_extra_pts)).fill(last_new_y_pos);\n\n\t\t\tnew_x = new_x.concat(filler_x);\n\t\t\tnew_y = new_y.concat(filler_y);\n\t\t}\n\n\t\treturn [old_x, old_y, new_x, new_y];\n\t}\n\n\tmake_anim_x_axis(height, text_start_at, axis_line_class) {\n\t\t// Animate X AXIS to account for more or less axis lines\n\n\t\tconst old_pos = this.x_old_axis_positions;\n\t\tconst new_pos = this.x_axis_positions;\n\n\t\tconst old_vals = this.old_x_values;\n\t\tconst new_vals = this.x;\n\n\t\tconst last_line_pos = old_pos[old_pos.length - 1];\n\n\t\tlet add_and_animate_line = (value, old_pos, new_pos) => {\n\t\t\tif(typeof new_pos === 'string') {\n\t\t\t\tnew_pos = parseInt(new_pos.substring(0, new_pos.length-1));\n\t\t\t}\n\t\t\tconst x_line = this.make_x_line(\n\t\t\t\theight,\n\t\t\t\ttext_start_at,\n\t\t\t\tvalue, // new value\n\t\t\t\t'x-value-text',\n\t\t\t\taxis_line_class,\n\t\t\t\told_pos // old position\n\t\t\t);\n\t\t\tthis.x_axis_group.appendChild(x_line);\n\n\t\t\tthis.elements_to_animate && this.elements_to_animate.push([\n\t\t\t\t{unit: x_line, array: [0], index: 0},\n\t\t\t\t{transform: `${ new_pos }, 0`},\n\t\t\t\t350,\n\t\t\t\t\"easein\",\n\t\t\t\t\"translate\",\n\t\t\t\t{transform: `${ old_pos }, 0`}\n\t\t\t]);\n\t\t};\n\n\t\tthis.x_axis_group.textContent = '';\n\n\t\tthis.make_new_axis_anim_lines(\n\t\t\told_pos,\n\t\t\tnew_pos,\n\t\t\told_vals,\n\t\t\tnew_vals,\n\t\t\tlast_line_pos,\n\t\t\tadd_and_animate_line\n\t\t);\n\t}\n\n\tmake_anim_y_axis() {\n\t\t// Animate Y AXIS to account for more or less axis lines\n\n\t\tconst old_pos = this.y_old_axis_values.map(value =>\n\t\t\tthis.zero_line - value * this.multiplier);\n\t\tconst new_pos = this.y_axis_values.map(value =>\n\t\t\tthis.zero_line - value * this.multiplier);\n\n\t\tconst old_vals = this.y_old_axis_values;\n\t\tconst new_vals = this.y_axis_values;\n\n\t\tconst last_line_pos = old_pos[old_pos.length - 1];\n\n\t\tthis.y_axis_group.textContent = '';\n\n\t\tthis.make_new_axis_anim_lines(\n\t\t\told_pos,\n\t\t\tnew_pos,\n\t\t\told_vals,\n\t\t\tnew_vals,\n\t\t\tlast_line_pos,\n\t\t\tthis.add_and_animate_y_line.bind(this),\n\t\t\tthis.y_axis_group\n\t\t);\n\t}\n\n\tmake_anim_y_specifics() {\n\t\tthis.specific_y_group.textContent = '';\n\t\tthis.specific_values.map((d) => {\n\t\t\tthis.add_and_animate_y_line(\n\t\t\t\td.title,\n\t\t\t\tthis.old_zero_line - d.value * this.old_multiplier,\n\t\t\t\tthis.zero_line - d.value * this.multiplier,\n\t\t\t\t0,\n\t\t\t\tthis.specific_y_group,\n\t\t\t\td.line_type,\n\t\t\t\ttrue\n\t\t\t);\n\t\t});\n\t}\n\n\tmake_new_axis_anim_lines(old_pos, new_pos, old_vals, new_vals, last_line_pos, add_and_animate_line, group) {\n\t\tlet superimposed_positions, superimposed_values;\n\t\tlet no_of_extras = new_vals.length - old_vals.length;\n\t\tif(no_of_extras > 0) {\n\t\t\t// More axis are needed\n\t\t\t// First make only the superimposed (same position) ones\n\t\t\t// Add in the extras at the end later\n\t\t\tsuperimposed_positions = new_pos.slice(0, old_pos.length);\n\t\t\tsuperimposed_values = new_vals.slice(0, old_vals.length);\n\t\t} else {\n\t\t\t// Axis have to be reduced\n\t\t\t// Fake it by moving all current extra axis to the last position\n\t\t\t// You'll need filler positions and values in the new arrays\n\t\t\tconst filler_vals = new Array(Math.abs(no_of_extras)).fill(\"\");\n\t\t\tsuperimposed_values = new_vals.concat(filler_vals);\n\n\t\t\tconst filler_pos = new Array(Math.abs(no_of_extras)).fill(last_line_pos + \"F\");\n\t\t\tsuperimposed_positions = new_pos.concat(filler_pos);\n\t\t}\n\n\t\tsuperimposed_values.map((value, i) => {\n\t\t\tadd_and_animate_line(value, old_pos[i], superimposed_positions[i], i, group);\n\t\t});\n\n\t\tif(no_of_extras > 0) {\n\t\t\t// Add in extra axis in the end\n\t\t\t// and then animate to new positions\n\t\t\tconst extra_values = new_vals.slice(old_vals.length);\n\t\t\tconst extra_positions = new_pos.slice(old_pos.length);\n\n\t\t\textra_values.map((value, i) => {\n\t\t\t\tadd_and_animate_line(value, last_line_pos, extra_positions[i], i, group);\n\t\t\t});\n\t\t}\n\t}\n\n\tmake_x_line(height, text_start_at, point, label_class, axis_line_class, x_pos) {\n\t\tlet line = $.createSVG('line', {\n\t\t\tx1: 0,\n\t\t\tx2: 0,\n\t\t\ty1: 0,\n\t\t\ty2: height\n\t\t});\n\n\t\tlet text = $.createSVG('text', {\n\t\t\tclassName: label_class,\n\t\t\tx: 0,\n\t\t\ty: text_start_at,\n\t\t\tdy: '.71em',\n\t\t\tinnerHTML: point\n\t\t});\n\n\t\tlet x_level = $.createSVG('g', {\n\t\t\tclassName: `tick ${axis_line_class}`,\n\t\t\ttransform: `translate(${ x_pos }, 0)`\n\t\t});\n\n\t\tx_level.appendChild(line);\n\t\tx_level.appendChild(text);\n\n\t\treturn x_level;\n\t}\n\n\tmake_y_line(start_at, width, text_end_at, point, label_class, axis_line_class, y_pos, darker=false, line_type=\"\") {\n\t\tlet line = $.createSVG('line', {\n\t\t\tclassName: line_type === \"dashed\" ? \"dashed\": \"\",\n\t\t\tx1: start_at,\n\t\t\tx2: width,\n\t\t\ty1: 0,\n\t\t\ty2: 0\n\t\t});\n\n\t\tlet text = $.createSVG('text', {\n\t\t\tclassName: label_class,\n\t\t\tx: text_end_at,\n\t\t\ty: 0,\n\t\t\tdy: '.32em',\n\t\t\tinnerHTML: point+\"\"\n\t\t});\n\n\t\tlet y_level = $.createSVG('g', {\n\t\t\tclassName: `tick ${axis_line_class}`,\n\t\t\ttransform: `translate(0, ${y_pos})`,\n\t\t\t'stroke-opacity': 1\n\t\t});\n\n\t\tif(darker) {\n\t\t\tline.style.stroke = \"rgba(27, 31, 35, 0.6)\";\n\t\t}\n\n\t\ty_level.appendChild(line);\n\t\ty_level.appendChild(text);\n\n\t\treturn y_level;\n\t}\n\n\tadd_and_animate_y_line(value, old_pos, new_pos, i, group, type, specific=false) {\n\t\tlet filler = false;\n\t\tif(typeof new_pos === 'string') {\n\t\t\tnew_pos = parseInt(new_pos.substring(0, new_pos.length-1));\n\t\t\tfiller = true;\n\t\t}\n\t\tlet new_props = {transform: `0, ${ new_pos }`};\n\t\tlet old_props = {transform: `0, ${ old_pos }`};\n\n\t\tif(filler) {\n\t\t\tnew_props['stroke-opacity'] = 0;\n\t\t\t// old_props['stroke-opacity'] = 1;\n\t\t}\n\n\t\tlet [width, text_end_at, axis_line_class, start_at] = this.get_y_axis_line_props(specific);\n\t\tlet axis_label_class = !specific ? 'y-value-text' : 'specific-value';\n\t\tvalue = !specific ? value : (value+\"\").toUpperCase();\n\t\tconst y_line = this.make_y_line(\n\t\t\tstart_at,\n\t\t\twidth,\n\t\t\ttext_end_at,\n\t\t\tvalue,\n\t\t\taxis_label_class,\n\t\t\taxis_line_class,\n\t\t\told_pos, // old position\n\t\t\t(value === 0 && i !== 0), // Non-first Zero line\n\t\t\ttype\n\t\t);\n\n\t\tgroup.appendChild(y_line);\n\n\t\tthis.elements_to_animate && this.elements_to_animate.push([\n\t\t\t{unit: y_line, array: [0], index: 0},\n\t\t\tnew_props,\n\t\t\t350,\n\t\t\t\"easein\",\n\t\t\t\"translate\",\n\t\t\told_props\n\t\t]);\n\t}\n\n\tget_y_axis_points(array) {\n\t\t//*** Where the magic happens ***\n\n\t\t// Calculates best-fit y intervals from given values\n\t\t// and returns the interval array\n\n\t\t// TODO: Fractions\n\n\t\tlet max_bound, min_bound, pos_no_of_parts, neg_no_of_parts, part_size; // eslint-disable-line no-unused-vars\n\n\t\t// Critical values\n\t\tlet max_val = parseInt(Math.max(...array));\n\t\tlet min_val = parseInt(Math.min(...array));\n\t\tif(min_val >= 0) {\n\t\t\tmin_val = 0;\n\t\t}\n\n\t\tlet get_params = (value1, value2) => {\n\t\t\tlet bound1, bound2, no_of_parts_1, no_of_parts_2, interval_size;\n\t\t\tif((value1+\"\").length <= 1) {\n\t\t\t\t[bound1, no_of_parts_1] = [10, 5];\n\t\t\t} else {\n\t\t\t\t[bound1, no_of_parts_1] = this.calc_upper_bound_and_no_of_parts(value1);\n\t\t\t}\n\n\t\t\tinterval_size = bound1 / no_of_parts_1;\n\t\t\tno_of_parts_2 = this.calc_no_of_parts(value2, interval_size);\n\t\t\tbound2 = no_of_parts_2 * interval_size;\n\n\t\t\treturn [bound1, bound2, no_of_parts_1, no_of_parts_2, interval_size];\n\t\t};\n\n\t\tconst abs_min_val = min_val * -1;\n\t\tif(abs_min_val <= max_val) {\n\t\t\t// Get the positive region intervals\n\t\t\t// then calc negative ones accordingly\n\t\t\t[max_bound, min_bound, pos_no_of_parts, neg_no_of_parts, part_size]\n\t\t\t\t= get_params(max_val, abs_min_val);\n\t\t\tif(abs_min_val === 0) {\n\t\t\t\tmin_bound = 0; neg_no_of_parts = 0;\n\t\t\t}\n\t\t} else {\n\t\t\t// Get the negative region here first\n\t\t\t[min_bound, max_bound, neg_no_of_parts, pos_no_of_parts, part_size]\n\t\t\t\t= get_params(abs_min_val, max_val);\n\t\t}\n\n\t\t// Make both region parts even\n\t\tif(pos_no_of_parts % 2 !== 0 && neg_no_of_parts > 0) pos_no_of_parts++;\n\t\tif(neg_no_of_parts % 2 !== 0) {\n\t\t\t// every increase in no_of_parts entails an increase in corresponding bound\n\t\t\t// except here, it happens implicitly after every calc_no_of_parts() call\n\t\t\tneg_no_of_parts++;\n\t\t\tmin_bound += part_size;\n\t\t}\n\n\t\tlet no_of_parts = pos_no_of_parts + neg_no_of_parts;\n\t\tif(no_of_parts > 5) {\n\t\t\tno_of_parts /= 2;\n\t\t\tpart_size *= 2;\n\n\t\t\tpos_no_of_parts /=2;\n\t\t}\n\n\t\tif (max_val < (pos_no_of_parts - 1) * part_size) {\n\t\t\tno_of_parts--;\n\t\t}\n\n\t\treturn this.get_intervals(\n\t\t\t(-1) * min_bound,\n\t\t\tpart_size,\n\t\t\tno_of_parts\n\t\t);\n\t}\n\n\tget_intervals(start, interval_size, count) {\n\t\tlet intervals = [];\n\t\tfor(var i = 0; i <= count; i++){\n\t\t\tintervals.push(start);\n\t\t\tstart += interval_size;\n\t\t}\n\t\treturn intervals;\n\t}\n\n\tcalc_upper_bound_and_no_of_parts(max_val) {\n\t\t// Given a positive value, calculates a nice-number upper bound\n\t\t// and a consequent optimal number of parts\n\n\t\tconst part_size = Math.pow(10, ((max_val+\"\").length - 1));\n\t\tconst no_of_parts = this.calc_no_of_parts(max_val, part_size);\n\n\t\t// Use it to get a nice even upper bound\n\t\tconst upper_bound = part_size * no_of_parts;\n\n\t\treturn [upper_bound, no_of_parts];\n\t}\n\n\tcalc_no_of_parts(value, divisor) {\n\t\t// value should be a positive number, divisor should be greater than 0\n\t\t// returns an even no of parts\n\t\tlet no_of_parts = Math.ceil(value / divisor);\n\t\tif(no_of_parts % 2 !== 0) no_of_parts++; // Make it an even number\n\n\t\treturn no_of_parts;\n\t}\n\n\tget_optimal_no_of_parts(no_of_parts) {\n\t\t// aka Divide by 2 if too large\n\t\treturn (no_of_parts < 5) ? no_of_parts : no_of_parts / 2;\n\t}\n\n\tset_avg_unit_width_and_x_offset() {\n\t\t// Set the ... you get it\n\t\tthis.avg_unit_width = this.width/(this.x.length - 1);\n\t\tthis.x_offset = 0;\n\t}\n\n\tget_all_y_values() {\n\t\tlet all_values = [];\n\n\t\t// Add in all the y values in the datasets\n\t\tthis.y.map(d => {\n\t\t\tall_values = all_values.concat(d.values);\n\t\t});\n\n\t\t// Add in all the specific values\n\t\treturn all_values.concat(this.specific_values.map(d => d.value));\n\t}\n\n\tcalc_y_dependencies() {\n\t\tthis.y_min_tops = new Array(this.x_axis_positions.length).fill(9999);\n\t\tthis.y.map(d => {\n\t\t\td.y_tops = d.values.map( val => float_2(this.zero_line - val * this.multiplier));\n\t\t\td.y_tops.map( (y_top, i) => {\n\t\t\t\tif(y_top < this.y_min_tops[i]) {\n\t\t\t\t\tthis.y_min_tops[i] = y_top;\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t\t// this.chart_wrapper.removeChild(this.tip.container);\n\t\t// this.make_tooltip();\n\t}\n\n\tget_bar_height_and_y_attr(y_top) {\n\t\tlet height, y;\n\t\tif (y_top <= this.zero_line) {\n\t\t\theight = this.zero_line - y_top;\n\t\t\ty = y_top;\n\n\t\t\t// In case of invisible bars\n\t\t\tif(height === 0) {\n\t\t\t\theight = this.height * 0.01;\n\t\t\t\ty -= height;\n\t\t\t}\n\t\t} else {\n\t\t\theight = y_top - this.zero_line;\n\t\t\ty = this.zero_line;\n\n\t\t\t// In case of invisible bars\n\t\t\tif(height === 0) {\n\t\t\t\theight = this.height * 0.01;\n\t\t\t}\n\t\t}\n\n\t\treturn [height, y];\n\t}\n\n\tsetup_utils() {\n\t\tthis.draw = {\n\t\t\t'bar': (x, y_top, args, color, index, dataset_index, no_of_datasets) => {\n\t\t\t\tlet total_width = this.avg_unit_width - args.space_width;\n\t\t\t\tlet start_x = x - total_width/2;\n\n\t\t\t\tlet width = total_width / no_of_datasets;\n\t\t\t\tlet current_x = start_x + width * dataset_index;\n\n\t\t\t\tlet [height, y] = this.get_bar_height_and_y_attr(y_top);\n\n\t\t\t\treturn $.createSVG('rect', {\n\t\t\t\t\tclassName: `bar mini fill ${color}`,\n\t\t\t\t\t'data-point-index': index,\n\t\t\t\t\tx: current_x,\n\t\t\t\t\ty: y,\n\t\t\t\t\twidth: width,\n\t\t\t\t\theight: height\n\t\t\t\t});\n\n\t\t\t},\n\t\t\t'dot': (x, y, args, color, index) => {\n\t\t\t\treturn $.createSVG('circle', {\n\t\t\t\t\tclassName: `fill ${color}`,\n\t\t\t\t\t'data-point-index': index,\n\t\t\t\t\tcx: x,\n\t\t\t\t\tcy: y,\n\t\t\t\t\tr: args.radius\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\n\t\tthis.animate = {\n\t\t\t'bar': (bar_obj, x, y_top, index) => {\n\t\t\t\tlet start = x - this.avg_unit_width/4;\n\t\t\t\tlet width = (this.avg_unit_width/2)/this.y.length;\n\t\t\t\tlet [height, y] = this.get_bar_height_and_y_attr(y_top);\n\n\t\t\t\tx = start + (width * index);\n\n\t\t\t\treturn [bar_obj, {width: width, height: height, x: x, y: y}, 350, \"easein\"];\n\t\t\t\t// bar.animate({height: args.new_height, y: y_top}, 350, mina.easein);\n\t\t\t},\n\t\t\t'dot': (dot_obj, x, y_top) => {\n\t\t\t\treturn [dot_obj, {cx: x, cy: y_top}, 350, \"easein\"];\n\t\t\t\t// dot.animate({cy: y_top}, 350, mina.easein);\n\t\t\t}\n\t\t};\n\t}\n}\n","import AxisChart from './AxisChart';\n\nexport default class BarChart extends AxisChart {\n\tconstructor(args) {\n\t\tsuper(args);\n\n\t\tthis.type = 'bar';\n\t\tthis.x_axis_mode = args.x_axis_mode || 'tick';\n\t\tthis.y_axis_mode = args.y_axis_mode || 'span';\n\t\tthis.setup();\n\t}\n\n\tsetup_values() {\n\t\tsuper.setup_values();\n\t\tthis.x_offset = this.avg_unit_width;\n\t\tthis.unit_args = {\n\t\t\ttype: 'bar',\n\t\t\targs: {\n\t\t\t\tspace_width: this.avg_unit_width/2,\n\t\t\t}\n\t\t};\n\t}\n\n\tmake_overlay() {\n\t\t// Just make one out of the first element\n\t\tlet index = this.x.length - 1;\n\t\tlet unit = this.y[0].svg_units[index];\n\t\tthis.update_current_data_point(index);\n\n\t\tif(this.overlay) {\n\t\t\tthis.overlay.parentNode.removeChild(this.overlay);\n\t\t}\n\n\t\tthis.overlay = unit.cloneNode();\n\t\tthis.overlay.style.fill = '#000000';\n\t\tthis.overlay.style.opacity = '0.4';\n\t\tthis.draw_area.appendChild(this.overlay);\n\t}\n\n\tbind_overlay() {\n\t\t// on event, update overlay\n\t\tthis.parent.addEventListener('data-select', (e) => {\n\t\t\tthis.update_overlay(e.svg_unit);\n\t\t});\n\t}\n\n\tbind_units(units_array) {\n\t\tunits_array.map(unit => {\n\t\t\tunit.addEventListener('click', () => {\n\t\t\t\tlet index = unit.getAttribute('data-point-index');\n\t\t\t\tthis.update_current_data_point(index);\n\t\t\t});\n\t\t});\n\t}\n\n\tupdate_overlay(unit) {\n\t\tlet attributes = [];\n\t\tObject.keys(unit.attributes).map(index => {\n\t\t\tattributes.push(unit.attributes[index]);\n\t\t});\n\n\t\tattributes.filter(attr => attr.specified).map(attr => {\n\t\t\tthis.overlay.setAttribute(attr.name, attr.nodeValue);\n\t\t});\n\t}\n\n\ton_left_arrow() {\n\t\tthis.update_current_data_point(this.current_index - 1);\n\t}\n\n\ton_right_arrow() {\n\t\tthis.update_current_data_point(this.current_index + 1);\n\t}\n\n\tset_avg_unit_width_and_x_offset() {\n\t\tthis.avg_unit_width = this.width/(this.x.length + 1);\n\t\tthis.x_offset = this.avg_unit_width;\n\t}\n}\n","import AxisChart from './AxisChart';\nimport $ from '../helpers/dom';\n\nexport default class LineChart extends AxisChart {\n\tconstructor(args) {\n\t\tsuper(args);\n\n\t\tthis.x_axis_mode = args.x_axis_mode || 'span';\n\t\tthis.y_axis_mode = args.y_axis_mode || 'span';\n\n\t\tif(args.hasOwnProperty('show_dots')) {\n\t\t\tthis.show_dots = args.show_dots;\n\t\t} else {\n\t\t\tthis.show_dots = 1;\n\t\t}\n\t\tthis.region_fill = args.region_fill;\n\n\t\tif(Object.getPrototypeOf(this) !== LineChart.prototype) {\n\t\t\treturn;\n\t\t}\n\t\tthis.dot_radius = args.dot_radius || 4;\n\t\tthis.heatline = args.heatline;\n\t\tthis.type = 'line';\n\n\t\tthis.setup();\n\t}\n\n\tsetup_graph_components() {\n\t\tthis.setup_path_groups();\n\t\tsuper.setup_graph_components();\n\t}\n\n\tsetup_path_groups() {\n\t\tthis.paths_groups = [];\n\t\tthis.y.map((d, i) => {\n\t\t\tthis.paths_groups[i] = $.createSVG('g', {\n\t\t\t\tclassName: 'path-group path-group-' + i,\n\t\t\t\tinside: this.draw_area\n\t\t\t});\n\t\t});\n\t}\n\n\tsetup_values() {\n\t\tsuper.setup_values();\n\t\tthis.unit_args = {\n\t\t\ttype: 'dot',\n\t\t\targs: { radius: this.dot_radius }\n\t\t};\n\t}\n\n\tmake_new_units_for_dataset(x_values, y_values, color, dataset_index,\n\t\tno_of_datasets, units_group, units_array, unit) {\n\t\tif(this.show_dots) {\n\t\t\tsuper.make_new_units_for_dataset(x_values, y_values, color, dataset_index,\n\t\t\t\tno_of_datasets, units_group, units_array, unit);\n\t\t}\n\t}\n\n\tmake_paths() {\n\t\tthis.y.map((d, i) => {\n\t\t\tthis.make_path(d, i, this.x_axis_positions, d.y_tops, d.color || this.colors[i]);\n\t\t});\n\t}\n\n\tmake_path(d, i, x_positions, y_positions, color) {\n\t\tlet points_list = y_positions.map((y, i) => (x_positions[i] + ',' + y));\n\t\tlet points_str = points_list.join(\"L\");\n\n\t\tthis.paths_groups[i].textContent = '';\n\n\t\td.path = $.createSVG('path', {\n\t\t\tinside: this.paths_groups[i],\n\t\t\tclassName: `stroke ${color}`,\n\t\t\td: \"M\"+points_str\n\t\t});\n\n\t\tif(this.heatline) {\n\t\t\tlet gradient_id = this.make_gradient(color);\n\t\t\td.path.style.stroke = `url(#${gradient_id})`;\n\t\t}\n\n\t\tif(this.region_fill) {\n\t\t\tthis.fill_region_for_dataset(d, i, color, points_str);\n\t\t}\n\t}\n\n\tfill_region_for_dataset(d, i, color, points_str) {\n\t\tlet gradient_id = this.make_gradient(color, true);\n\n\t\td.region_path = $.createSVG('path', {\n\t\t\tinside: this.paths_groups[i],\n\t\t\tclassName: `region-fill`,\n\t\t\td: \"M\" + `0,${this.zero_line}L` + points_str + `L${this.width},${this.zero_line}`,\n\t\t});\n\n\t\td.region_path.style.stroke = \"none\";\n\t\td.region_path.style.fill = `url(#${gradient_id})`;\n\t}\n\n\tmake_gradient(color, lighter = false) {\n\t\tlet gradient_id ='path-fill-gradient' + '-' + color;\n\n\t\tlet gradient_def = $.createSVG('linearGradient', {\n\t\t\tinside: this.svg_defs,\n\t\t\tid: gradient_id,\n\t\t\tx1: 0,\n\t\t\tx2: 0,\n\t\t\ty1: 0,\n\t\t\ty2: 1\n\t\t});\n\n\t\tlet set_gradient_stop = (grad_elem, offset, color, opacity) => {\n\t\t\t$.createSVG('stop', {\n\t\t\t\t'className': 'stop-color ' + color,\n\t\t\t\t'inside': grad_elem,\n\t\t\t\t'offset': offset,\n\t\t\t\t'stop-opacity': opacity\n\t\t\t});\n\t\t};\n\n\t\tlet opacities = [1, 0.6, 0.2];\n\n\t\tif(lighter) {\n\t\t\topacities = [0.4, 0.2, 0];\n\t\t}\n\n\t\tset_gradient_stop(gradient_def, \"0%\", color, opacities[0]);\n\t\tset_gradient_stop(gradient_def, \"50%\", color, opacities[1]);\n\t\tset_gradient_stop(gradient_def, \"100%\", color, opacities[2]);\n\n\t\treturn gradient_id;\n\t}\n}\n","import LineChart from './LineChart';\n\nexport default class ScatterChart extends LineChart {\n\tconstructor(args) {\n\t\tsuper(args);\n\n\t\tthis.type = 'scatter';\n\n\t\tif(!args.dot_radius) {\n\t\t\tthis.dot_radius = 8;\n\t\t} else {\n\t\t\tthis.dot_radius = args.dot_radius;\n\t\t}\n\n\t\tthis.setup();\n\t}\n\n\tsetup_graph_components() {\n\t\tthis.setup_path_groups();\n\t\tsuper.setup_graph_components();\n\t}\n\n\tsetup_path_groups() {}\n\n\tsetup_values() {\n\t\tsuper.setup_values();\n\t\tthis.unit_args = {\n\t\t\ttype: 'dot',\n\t\t\targs: { radius: this.dot_radius }\n\t\t};\n\t}\n\n\tmake_paths() {}\n\tmake_path() {}\n}\n","import BaseChart from './BaseChart';\nimport $ from '../helpers/dom';\n\nexport default class PercentageChart extends BaseChart {\n\tconstructor(args) {\n\t\tsuper(args);\n\t\tthis.type = 'percentage';\n\n\t\tthis.get_y_label = this.format_lambdas.y_label;\n\t\tthis.get_x_tooltip = this.format_lambdas.x_tooltip;\n\t\tthis.get_y_tooltip = this.format_lambdas.y_tooltip;\n\n\t\tthis.max_slices = 10;\n\t\tthis.max_legend_points = 6;\n\n\t\tthis.colors = args.colors;\n\n\t\tif(!this.colors || this.colors.length < this.data.labels.length) {\n\t\t\tthis.colors = ['light-blue', 'blue', 'violet', 'red', 'orange',\n\t\t\t\t'yellow', 'green', 'light-green', 'purple', 'magenta'];\n\t\t}\n\n\t\tthis.setup();\n\t}\n\n\tmake_chart_area() {\n\t\tthis.chart_wrapper.className += ' ' + 'graph-focus-margin';\n\t\tthis.chart_wrapper.style.marginTop = '45px';\n\n\t\tthis.stats_wrapper.className += ' ' + 'graph-focus-margin';\n\t\tthis.stats_wrapper.style.marginBottom = '30px';\n\t\tthis.stats_wrapper.style.paddingTop = '0px';\n\t}\n\n\tmake_draw_area() {\n\t\tthis.chart_div = $.create('div', {\n\t\t\tclassName: 'div',\n\t\t\tinside: this.chart_wrapper,\n\t\t\twidth: this.base_width,\n\t\t\theight: this.base_height\n\t\t});\n\n\t\tthis.chart = $.create('div', {\n\t\t\tclassName: 'progress-chart',\n\t\t\tinside: this.chart_div\n\t\t});\n\t}\n\n\tsetup_components() {\n\t\tthis.percentage_bar = $.create('div', {\n\t\t\tclassName: 'progress',\n\t\t\tinside: this.chart\n\t\t});\n\t}\n\n\tsetup_values() {\n\t\tthis.slice_totals = [];\n\t\tlet all_totals = this.data.labels.map((d, i) => {\n\t\t\tlet total = 0;\n\t\t\tthis.data.datasets.map(e => {\n\t\t\t\ttotal += e.values[i];\n\t\t\t});\n\t\t\treturn [total, d];\n\t\t}).filter(d => { return d[0] > 0; }); // keep only positive results\n\n\t\tlet totals = all_totals;\n\n\t\tif(all_totals.length > this.max_slices) {\n\t\t\tall_totals.sort((a, b) => { return b[0] - a[0]; });\n\n\t\t\ttotals = all_totals.slice(0, this.max_slices-1);\n\t\t\tlet others = all_totals.slice(this.max_slices-1);\n\n\t\t\tlet sum_of_others = 0;\n\t\t\tothers.map(d => {sum_of_others += d[0];});\n\n\t\t\ttotals.push([sum_of_others, 'Rest']);\n\n\t\t\tthis.colors[this.max_slices-1] = 'grey';\n\t\t}\n\n\t\tthis.labels = [];\n\t\ttotals.map(d => {\n\t\t\tthis.slice_totals.push(d[0]);\n\t\t\tthis.labels.push(d[1]);\n\t\t});\n\n\t\tthis.legend_totals = this.slice_totals.slice(0, this.max_legend_points);\n\t}\n\n\tsetup_utils() { }\n\n\tmake_graph_components() {\n\t\tthis.grand_total = this.slice_totals.reduce((a, b) => a + b, 0);\n\t\tthis.slices = [];\n\t\tthis.slice_totals.map((total, i) => {\n\t\t\tlet slice = $.create('div', {\n\t\t\t\tclassName: `progress-bar background ${this.colors[i]}`,\n\t\t\t\tstyle: `width: ${total*100/this.grand_total}%`,\n\t\t\t\tinside: this.percentage_bar\n\t\t\t});\n\t\t\tthis.slices.push(slice);\n\t\t});\n\t}\n\n\tbind_tooltip() {\n\t\tthis.slices.map((slice, i) => {\n\t\t\tslice.addEventListener('mouseenter', () => {\n\t\t\t\tlet g_off = $.offset(this.chart_wrapper), p_off = $.offset(slice);\n\n\t\t\t\tlet x = p_off.left - g_off.left + slice.offsetWidth/2;\n\t\t\t\tlet y = p_off.top - g_off.top - 6;\n\t\t\t\tlet title = (this.formatted_labels && this.formatted_labels.length>0\n\t\t\t\t\t? this.formatted_labels[i] : this.labels[i]) + ': ';\n\t\t\t\tlet percent = (this.slice_totals[i]*100/this.grand_total).toFixed(1);\n\n\t\t\t\tthis.tip.set_values(x, y, title, percent + \"%\");\n\t\t\t\tthis.tip.show_tip();\n\t\t\t});\n\t\t});\n\t}\n\n\tshow_summary() {\n\t\tlet x_values = this.formatted_labels && this.formatted_labels.length > 0\n\t\t\t? this.formatted_labels : this.labels;\n\t\tthis.legend_totals.map((d, i) => {\n\t\t\tif(d) {\n\t\t\t\tlet stats = $.create('div', {\n\t\t\t\t\tclassName: 'stats',\n\t\t\t\t\tinside: this.stats_wrapper\n\t\t\t\t});\n\t\t\t\tstats.innerHTML = `\n\t\t\t\t\t${x_values[i]}:\n\t\t\t\t\t${d}\n\t\t\t\t`;\n\t\t\t}\n\t\t});\n\t}\n}\n","import BaseChart from './BaseChart';\nimport $ from '../helpers/dom';\n\nexport default class Heatmap extends BaseChart {\n\tconstructor({\n\t\tstart = '',\n\t\tdomain = '',\n\t\tsubdomain = '',\n\t\tdata = {},\n\t\tdiscrete_domains = 0,\n\t\tcount_label = ''\n\t}) {\n\t\tsuper(arguments[0]);\n\n\t\tthis.type = 'heatmap';\n\n\t\tthis.domain = domain;\n\t\tthis.subdomain = subdomain;\n\t\tthis.data = data;\n\t\tthis.discrete_domains = discrete_domains;\n\t\tthis.count_label = count_label;\n\n\t\tlet today = new Date();\n\t\tthis.start = start || this.add_days(today, 365);\n\n\t\tthis.legend_colors = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];\n\n\t\tthis.translate_x = 0;\n\t\tthis.setup();\n\t}\n\n\tsetup_base_values() {\n\t\tthis.today = new Date();\n\n\t\tif(!this.start) {\n\t\t\tthis.start = new Date();\n\t\t\tthis.start.setFullYear( this.start.getFullYear() - 1 );\n\t\t}\n\t\tthis.first_week_start = new Date(this.start.toDateString());\n\t\tthis.last_week_start = new Date(this.today.toDateString());\n\t\tif(this.first_week_start.getDay() !== 7) {\n\t\t\tthis.add_days(this.first_week_start, (-1) * this.first_week_start.getDay());\n\t\t}\n\t\tif(this.last_week_start.getDay() !== 7) {\n\t\t\tthis.add_days(this.last_week_start, (-1) * this.last_week_start.getDay());\n\t\t}\n\t\tthis.no_of_cols = this.get_weeks_between(this.first_week_start + '', this.last_week_start + '') + 1;\n\t}\n\n\tset_width() {\n\t\tthis.base_width = (this.no_of_cols) * 12;\n\n\t\tif(this.discrete_domains) {\n\t\t\tthis.base_width += (12 * 12);\n\t\t}\n\t}\n\n\tsetup_components() {\n\t\tthis.domain_label_group = $.createSVG(\"g\", {\n\t\t\tclassName: \"domain-label-group chart-label\",\n\t\t\tinside: this.draw_area\n\t\t});\n\t\tthis.data_groups = $.createSVG(\"g\", {\n\t\t\tclassName: \"data-groups\",\n\t\t\tinside: this.draw_area,\n\t\t\ttransform: `translate(0, 20)`\n\t\t});\n\t}\n\n\tsetup_values() {\n\t\tthis.domain_label_group.textContent = '';\n\t\tthis.data_groups.textContent = '';\n\t\tthis.distribution = this.get_distribution(this.data, this.legend_colors);\n\t\tthis.month_names = [\"January\", \"February\", \"March\", \"April\", \"May\", \"June\",\n\t\t\t\"July\", \"August\", \"September\", \"October\", \"November\", \"December\"\n\t\t];\n\n\t\tthis.render_all_weeks_and_store_x_values(this.no_of_cols);\n\t}\n\n\trender_all_weeks_and_store_x_values(no_of_weeks) {\n\t\tlet current_week_sunday = new Date(this.first_week_start);\n\t\tthis.week_col = 0;\n\t\tthis.current_month = current_week_sunday.getMonth();\n\n\t\tthis.months = [this.current_month + ''];\n\t\tthis.month_weeks = {}, this.month_start_points = [];\n\t\tthis.month_weeks[this.current_month] = 0;\n\t\tthis.month_start_points.push(13);\n\n\t\tfor(var i = 0; i < no_of_weeks; i++) {\n\t\t\tlet data_group, month_change = 0;\n\t\t\tlet day = new Date(current_week_sunday);\n\n\t\t\t[data_group, month_change] = this.get_week_squares_group(day, this.week_col);\n\t\t\tthis.data_groups.appendChild(data_group);\n\t\t\tthis.week_col += 1 + parseInt(this.discrete_domains && month_change);\n\t\t\tthis.month_weeks[this.current_month]++;\n\t\t\tif(month_change) {\n\t\t\t\tthis.current_month = (this.current_month + 1) % 12;\n\t\t\t\tthis.months.push(this.current_month + '');\n\t\t\t\tthis.month_weeks[this.current_month] = 1;\n\t\t\t}\n\t\t\tthis.add_days(current_week_sunday, 7);\n\t\t}\n\t\tthis.render_month_labels();\n\t}\n\n\tget_week_squares_group(current_date, index) {\n\t\tconst no_of_weekdays = 7;\n\t\tconst square_side = 10;\n\t\tconst cell_padding = 2;\n\t\tconst step = 1;\n\n\t\tlet month_change = 0;\n\t\tlet week_col_change = 0;\n\n\t\tlet data_group = $.createSVG(\"g\", {\n\t\t\tclassName: \"data-group\",\n\t\t\tinside: this.data_groups\n\t\t});\n\n\t\tfor(var y = 0, i = 0; i < no_of_weekdays; i += step, y += (square_side + cell_padding)) {\n\t\t\tlet data_value = 0;\n\t\t\tlet color_index = 0;\n\n\t\t\tlet timestamp = Math.floor(current_date.getTime()/1000).toFixed(1);\n\n\t\t\tif(this.data[timestamp]) {\n\t\t\t\tdata_value = this.data[timestamp];\n\t\t\t\tcolor_index = this.get_max_checkpoint(data_value, this.distribution);\n\t\t\t}\n\n\t\t\tif(this.data[Math.round(timestamp)]) {\n\t\t\t\tdata_value = this.data[Math.round(timestamp)];\n\t\t\t\tcolor_index = this.get_max_checkpoint(data_value, this.distribution);\n\t\t\t}\n\n\t\t\tlet x = 13 + (index + week_col_change) * 12;\n\n\t\t\t$.createSVG(\"rect\", {\n\t\t\t\tclassName: 'day',\n\t\t\t\tinside: data_group,\n\t\t\t\tx: x,\n\t\t\t\ty: y,\n\t\t\t\twidth: square_side,\n\t\t\t\theight: square_side,\n\t\t\t\tfill: this.legend_colors[color_index],\n\t\t\t\t'data-date': this.get_dd_mm_yyyy(current_date),\n\t\t\t\t'data-value': data_value,\n\t\t\t\t'data-day': current_date.getDay()\n\t\t\t});\n\n\t\t\tlet next_date = new Date(current_date);\n\t\t\tthis.add_days(next_date, 1);\n\t\t\tif(next_date.getMonth() - current_date.getMonth()) {\n\t\t\t\tmonth_change = 1;\n\t\t\t\tif(this.discrete_domains) {\n\t\t\t\t\tweek_col_change = 1;\n\t\t\t\t}\n\n\t\t\t\tthis.month_start_points.push(13 + (index + week_col_change) * 12);\n\t\t\t}\n\t\t\tcurrent_date = next_date;\n\t\t}\n\n\t\treturn [data_group, month_change];\n\t}\n\n\trender_month_labels() {\n\t\t// this.first_month_label = 1;\n\t\t// if (this.first_week_start.getDate() > 8) {\n\t\t// \tthis.first_month_label = 0;\n\t\t// }\n\t\t// this.last_month_label = 1;\n\n\t\t// let first_month = this.months.shift();\n\t\t// let first_month_start = this.month_start_points.shift();\n\t\t// render first month if\n\n\t\t// let last_month = this.months.pop();\n\t\t// let last_month_start = this.month_start_points.pop();\n\t\t// render last month if\n\n\t\tthis.months.shift();\n\t\tthis.month_start_points.shift();\n\t\tthis.months.pop();\n\t\tthis.month_start_points.pop();\n\n\t\tthis.month_start_points.map((start, i) => {\n\t\t\tlet month_name = this.month_names[this.months[i]].substring(0, 3);\n\n\t\t\t$.createSVG('text', {\n\t\t\t\tclassName: 'y-value-text',\n\t\t\t\tinside: this.domain_label_group,\n\t\t\t\tx: start + 12,\n\t\t\t\ty: 10,\n\t\t\t\tdy: '.32em',\n\t\t\t\tinnerHTML: month_name\n\t\t\t});\n\n\t\t});\n\t}\n\n\tmake_graph_components() {\n\t\tArray.prototype.slice.call(\n\t\t\tthis.container.querySelectorAll('.graph-stats-container, .sub-title, .title')\n\t\t).map(d => {\n\t\t\td.style.display = 'None';\n\t\t});\n\t\tthis.chart_wrapper.style.marginTop = '0px';\n\t\tthis.chart_wrapper.style.paddingTop = '0px';\n\t}\n\n\tbind_tooltip() {\n\t\tArray.prototype.slice.call(\n\t\t\tdocument.querySelectorAll(\".data-group .day\")\n\t\t).map(el => {\n\t\t\tel.addEventListener('mouseenter', (e) => {\n\t\t\t\tlet count = e.target.getAttribute('data-value');\n\t\t\t\tlet date_parts = e.target.getAttribute('data-date').split('-');\n\n\t\t\t\tlet month = this.month_names[parseInt(date_parts[1])-1].substring(0, 3);\n\n\t\t\t\tlet g_off = this.chart_wrapper.getBoundingClientRect(), p_off = e.target.getBoundingClientRect();\n\n\t\t\t\tlet width = parseInt(e.target.getAttribute('width'));\n\t\t\t\tlet x = p_off.left - g_off.left + (width+2)/2;\n\t\t\t\tlet y = p_off.top - g_off.top - (width+2)/2;\n\t\t\t\tlet value = count + ' ' + this.count_label;\n\t\t\t\tlet name = ' on ' + month + ' ' + date_parts[0] + ', ' + date_parts[2];\n\n\t\t\t\tthis.tip.set_values(x, y, name, value, [], 1);\n\t\t\t\tthis.tip.show_tip();\n\t\t\t});\n\t\t});\n\t}\n\n\tupdate(data) {\n\t\tthis.data = data;\n\t\tthis.setup_values();\n\t\tthis.bind_tooltip();\n\t}\n\n\tget_distribution(data={}, mapper_array) {\n\t\tlet data_values = Object.keys(data).map(key => data[key]);\n\t\tlet data_max_value = Math.max(...data_values);\n\n\t\tlet distribution_step = 1 / (mapper_array.length - 1);\n\t\tlet distribution = [];\n\n\t\tmapper_array.map((color, i) => {\n\t\t\tlet checkpoint = data_max_value * (distribution_step * i);\n\t\t\tdistribution.push(checkpoint);\n\t\t});\n\n\t\treturn distribution;\n\t}\n\n\tget_max_checkpoint(value, distribution) {\n\t\treturn distribution.filter((d, i) => {\n\t\t\tif(i === 1) {\n\t\t\t\treturn distribution[0] < value;\n\t\t\t}\n\t\t\treturn d <= value;\n\t\t}).length - 1;\n\t}\n\n\t// TODO: date utils, move these out\n\n\t// https://stackoverflow.com/a/11252167/6495043\n\ttreat_as_utc(date_str) {\n\t\tlet result = new Date(date_str);\n\t\tresult.setMinutes(result.getMinutes() - result.getTimezoneOffset());\n\t\treturn result;\n\t}\n\n\tget_dd_mm_yyyy(date) {\n\t\tlet dd = date.getDate();\n\t\tlet mm = date.getMonth() + 1; // getMonth() is zero-based\n\t\treturn [\n\t\t\t(dd>9 ? '' : '0') + dd,\n\t\t\t(mm>9 ? '' : '0') + mm,\n\t\t\tdate.getFullYear()\n\t\t].join('-');\n\t}\n\n\tget_weeks_between(start_date_str, end_date_str) {\n\t\treturn Math.ceil(this.get_days_between(start_date_str, end_date_str) / 7);\n\t}\n\n\tget_days_between(start_date_str, end_date_str) {\n\t\tlet milliseconds_per_day = 24 * 60 * 60 * 1000;\n\t\treturn (this.treat_as_utc(end_date_str) - this.treat_as_utc(start_date_str)) / milliseconds_per_day;\n\t}\n\n\t// mutates\n\tadd_days(date, number_of_days) {\n\t\tdate.setDate(date.getDate() + number_of_days);\n\t}\n\n\tget_month_name() {}\n}\n","import '../styles/charts.less';\n\nimport BarChart from './charts/BarChart';\nimport LineChart from './charts/LineChart';\nimport ScatterChart from './charts/ScatterChart';\nimport PercentageChart from './charts/PercentageChart';\nimport Heatmap from './charts/Heatmap';\n\n// if (ENV !== 'production') {\n// \t// Enable LiveReload\n// \tdocument.write(\n// \t\t' diff --git a/package.json b/package.json index 59845c4..74f421f 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "charts", + "name": "frappe-charts", "version": "0.0.1", "description": "https://frappe.github.io/charts", "main": "dist/frappe-charts.js", diff --git a/rollup.config.js b/rollup.config.js index 95fa622..a3e8381 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -10,35 +10,69 @@ import nested from 'postcss-nested'; import cssnext from 'postcss-cssnext'; import cssnano from 'cssnano'; -export default { - input: 'src/scripts/charts.js', - output: { - file: 'dist/frappe-charts.min.js', - format: 'iife', +export default [ + { + input: 'src/scripts/charts.js', + output: { + file: 'dist/frappe-charts.min.js', + format: 'iife', + }, + name: 'Chart', + sourcemap: 'true', + plugins: [ + postcss({ + extensions: [ '.less' ], + plugins: [ + nested(), + cssnext({ warnForDuplicates: false }), + cssnano() + ] + }), + eslint({ + exclude: [ + 'src/styles/**', + ] + }), + babel({ + exclude: 'node_modules/**', + }), + replace({ + exclude: 'node_modules/**', + ENV: JSON.stringify(process.env.NODE_ENV || 'development'), + }), + uglify() + ], }, - name: 'Chart', - sourcemap: 'true', - plugins: [ - postcss({ - extensions: [ '.less' ], - plugins: [ - nested(), - cssnext({ warnForDuplicates: false }), - cssnano() - ] - }), - eslint({ - exclude: [ - 'src/styles/**', - ] - }), - babel({ - exclude: 'node_modules/**', - }), - replace({ - exclude: 'node_modules/**', - ENV: JSON.stringify(process.env.NODE_ENV || 'development'), - }), - uglify() - ], -}; + { + input: 'src/scripts/charts.js', + output: { + file: 'docs/assets/js/frappe-charts.min.js', + format: 'iife', + }, + name: 'Chart', + sourcemap: 'false', + plugins: [ + postcss({ + extensions: [ '.less' ], + plugins: [ + nested(), + cssnext({ warnForDuplicates: false }), + cssnano() + ] + }), + eslint({ + exclude: [ + 'src/styles/**', + ] + }), + babel({ + exclude: 'node_modules/**', + }), + replace({ + exclude: 'node_modules/**', + ENV: JSON.stringify(process.env.NODE_ENV || 'development'), + }), + uglify() + ], + } +]; diff --git a/src/scripts/charts/AxisChart.js b/src/scripts/charts/AxisChart.js index 22eecd5..3f16c38 100644 --- a/src/scripts/charts/AxisChart.js +++ b/src/scripts/charts/AxisChart.js @@ -137,7 +137,7 @@ export default class AxisChart extends BaseChart { this.x_axis_group.textContent = ''; this.x.map((point, i) => { - let space_taken = this.get_strwidth(point); + let space_taken = this.get_strwidth(point) + 2; if(space_taken > allowed_space) { if(this.is_series) { // Skip some axis lines if X axis is a series diff --git a/src/scripts/charts/PercentageChart.js b/src/scripts/charts/PercentageChart.js index 3b5299a..9d34366 100644 --- a/src/scripts/charts/PercentageChart.js +++ b/src/scripts/charts/PercentageChart.js @@ -35,9 +35,7 @@ export default class PercentageChart extends BaseChart { make_draw_area() { this.chart_div = $.create('div', { className: 'div', - inside: this.chart_wrapper, - width: this.base_width, - height: this.base_height + inside: this.chart_wrapper }); this.chart = $.create('div', { @@ -96,8 +94,10 @@ export default class PercentageChart extends BaseChart { this.slice_totals.map((total, i) => { let slice = $.create('div', { className: `progress-bar background ${this.colors[i]}`, - style: `width: ${total*100/this.grand_total}%`, - inside: this.percentage_bar + inside: this.percentage_bar, + styles: { + width: total*100/this.grand_total + "%" + } }); this.slices.push(slice); }); diff --git a/src/scripts/helpers/dom.js b/src/scripts/helpers/dom.js index 997a4ab..8b8917c 100644 --- a/src/scripts/helpers/dom.js +++ b/src/scripts/helpers/dom.js @@ -25,8 +25,13 @@ $.create = (tag, o) => { var ref = $(val); ref.parentNode.insertBefore(element, ref); element.appendChild(ref); - } - else if (i in element) { + } else if (i === "styles") { + if(typeof val === "object") { + Object.keys(val).map(prop => { + element.style[prop] = val[prop]; + }); + } + } else if (i in element ) { element[i] = val; } else {