require "json" require "erb" module Chartkick module Helper def line_chart(data_source, **options) chartkick_chart "LineChart", data_source, **options end def pie_chart(data_source, **options) chartkick_chart "PieChart", data_source, **options end def column_chart(data_source, **options) chartkick_chart "ColumnChart", data_source, **options end def bar_chart(data_source, **options) chartkick_chart "BarChart", data_source, **options end def area_chart(data_source, **options) chartkick_chart "AreaChart", data_source, **options end def scatter_chart(data_source, **options) chartkick_chart "ScatterChart", data_source, **options end def geo_chart(data_source, **options) chartkick_chart "GeoChart", data_source, **options end def timeline(data_source, **options) chartkick_chart "Timeline", data_source, **options end private def chartkick_chart(klass, data_source, **options) @chartkick_chart_id ||= 0 options = chartkick_deep_merge(Chartkick.options, options) element_id = options.delete(:id) || "chart-#{@chartkick_chart_id += 1}" height = (options.delete(:height) || "300px").to_s width = (options.delete(:width) || "100%").to_s defer = !!options.delete(:defer) # content_for: nil must override default content_for = options.key?(:content_for) ? options.delete(:content_for) : Chartkick.content_for nonce = options.delete(:nonce) if nonce == true # Secure Headers also defines content_security_policy_nonce but it takes an argument # Rails 5.2 overrides this method, but earlier versions do not if respond_to?(:content_security_policy_nonce) && (content_security_policy_nonce rescue nil) # Rails 5.2 nonce = content_security_policy_nonce elsif respond_to?(:content_security_policy_script_nonce) # Secure Headers nonce = content_security_policy_script_nonce end end nonce_html = nonce ? " nonce=\"#{ERB::Util.html_escape(nonce)}\"" : nil # html vars html_vars = { id: element_id } html_vars.each_key do |k| html_vars[k] = ERB::Util.html_escape(html_vars[k]) end # css vars css_vars = { height: height, width: width } css_vars.each_key do |k| # limit to alphanumeric and % for simplicity # this prevents things like calc() but safety is the priority raise ArgumentError, "Invalid #{k}" unless css_vars[k] =~ /\A[a-zA-Z0-9%]*\z/ # we limit above, but escape for safety as fail-safe # to prevent XSS injection in worse-case scenario css_vars[k] = ERB::Util.html_escape(css_vars[k]) end html = (options.delete(:html) || %(