asciidoctor-chart 1.0.0.alpha.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor
4
+ module Chart
5
+ module Converter
6
+ class Html5ChartjsConverter < Asciidoctor::Chart::Converter::Base
7
+ CSS_VALUE_UNIT_RX = /^([+-]?(?:\d+|\d*\.\d+))([a-z]*|%)$/.freeze
8
+ DEFAULT_COLORS = [{ r: 220, g: 220, b: 220 }, { r: 151, g: 187, b: 205 }].freeze
9
+
10
+ def convert_line_chart node
11
+ data, labels = prepare_data node
12
+ datasets = data.map do |set|
13
+ color = DEFAULT_COLORS[data.index(set) % 2]
14
+ color_rgba = "rgba(#{color[:r]},#{color[:g]},#{color[:b]},1.0)"
15
+ <<~JSON
16
+ {
17
+ borderColor: "#{color_rgba}",
18
+ backgroundColor: "#{color_rgba}",
19
+ fill: false,
20
+ tension: 0.1,
21
+ data: #{set.to_s}
22
+ }
23
+ JSON
24
+ end.join ','
25
+
26
+ chart_id = node.attr 'id'
27
+ inline_styles = []
28
+ if (chart_height = get_height node)
29
+ inline_styles.push("height: #{chart_height}")
30
+ end
31
+ if (chart_width = get_width node)
32
+ inline_styles.push("max-width: #{chart_width}")
33
+ end
34
+ maintain_aspect_ratio = chart_height.nil? && chart_width.nil?
35
+ title_element = node.title? ? %(\n <div class="title">#{node.captioned_title}</div>) : ''
36
+ <<~HTML
37
+ <div class="chartblock">
38
+ <div class="content chartjs-content" style="#{inline_styles.join('; ')}">
39
+ <canvas id="#{chart_id}"></canvas>
40
+ </div>#{title_element}
41
+ </div>
42
+ <script>
43
+ window.addEventListener('load', function(event) {
44
+ var data = {
45
+ labels: #{labels.to_s},
46
+ datasets: [#{datasets}]
47
+ }
48
+ var chart = new Chart(document.getElementById("#{chart_id}").getContext("2d"), {
49
+ type: 'line',
50
+ data: data,
51
+ options: {
52
+ interaction: {
53
+ mode: 'index'
54
+ },
55
+ responsive : true,
56
+ maintainAspectRatio: #{maintain_aspect_ratio},
57
+ plugins: {
58
+ legend: {
59
+ display: false
60
+ }
61
+ }
62
+ }
63
+ })
64
+ })
65
+ </script>
66
+ HTML
67
+ end
68
+
69
+ def prepare_data node
70
+ raw_data = node.attr 'data-raw', []
71
+ return [[], []] if raw_data.length <= 1 # question: should we warn?
72
+
73
+ labels = raw_data[0]
74
+ raw_data.shift
75
+ [raw_data, labels]
76
+ end
77
+
78
+ private
79
+
80
+ def get_height node
81
+ return unless (height = node.attr 'height')
82
+
83
+ to_css_size height
84
+ end
85
+
86
+ def get_width node
87
+ return unless (width = node.attr 'width')
88
+
89
+ to_css_size width
90
+ end
91
+
92
+ def to_css_size str
93
+ return str unless (parts = str.match(CSS_VALUE_UNIT_RX))
94
+
95
+ value, unit = parts.captures
96
+ unit = 'px' if unit == ''
97
+ "#{value}#{unit}"
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor
4
+ module Chart
5
+ module Converter
6
+ class Base
7
+ def handles? type
8
+ respond_to? %(convert_#{type}_chart)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -6,26 +6,75 @@ module Asciidoctor
6
6
  use_dsl
7
7
  # at_location :head
8
8
 
9
- C3JS_STYLESHEET = '<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/c3/0.3.0/c3.min.css">'
10
- D3JS_SCRIPT = '<script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js" charset="utf-8"></script>'
11
- C3JS_SCRIPT = '<script src="http://cdnjs.cloudflare.com/ajax/libs/c3/0.3.0/c3.min.js"></script>'
12
-
13
- CHARTIST_STYLESHEET = '<link rel="stylesheet" href="http://cdn.jsdelivr.net/chartist.js/latest/chartist.min.css">'
14
- CHARTIST_SCRIPT = '<script src="http://cdn.jsdelivr.net/chartist.js/latest/chartist.min.js"></script>'
15
-
16
- CHARTJS_SCRIPT = '<script src="http://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js"></script>'
17
-
18
- def process _doc
19
- # TODO: Import only the required engines
20
- # TODO: Honor linkcss and copycss
21
- <<~EOS
22
- #{C3JS_STYLESHEET}
23
- #{D3JS_SCRIPT}
24
- #{C3JS_SCRIPT}
25
- #{CHARTIST_STYLESHEET}
26
- #{CHARTIST_SCRIPT}
27
- #{CHARTJS_SCRIPT})
28
- EOS
9
+ C3JS_DIR_ATTR = 'c3jsdir'
10
+ C3JS_DEFAULT_PATH = 'https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.20/'
11
+
12
+ CHARTJS_DIR_ATTR = 'chartjsdir'
13
+ CHARTJS_DEFAULT_PATH = 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/'
14
+
15
+ CHARTIST_DIR_ATTR = 'chartistdir'
16
+ CHARTIST_DEFAULT_PATH = 'https://cdn.jsdelivr.net/npm/chartist@0.11.x/dist/'
17
+
18
+ D3JS_DIR_ATTR = 'd3jsdir'
19
+ D3JS_DEFAULT_PATH = 'https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/'
20
+
21
+ DEFAULT_STYLE = <<~HTML.chomp
22
+ <style>
23
+ .chartblock {
24
+ margin-bottom: 1.25em;
25
+ }
26
+ .chartblock > .title {
27
+ text-rendering: optimizeLegibility;
28
+ text-align: left;
29
+ font-size: 1rem;
30
+ font-style: italic;
31
+ line-height: 1.45;
32
+ color: #7a2518;
33
+ font-weight: 400;
34
+ }
35
+ .chartblock > .chartjs-content {
36
+ position: relative;
37
+ margin-bottom: 0.25em;
38
+ }
39
+ </style>
40
+ HTML
41
+
42
+ def process(doc)
43
+ engines = doc.x_chart[:engines]
44
+ (engines.map {|engine| send(engine, doc) }.to_a + [DEFAULT_STYLE]).join "\n"
45
+ end
46
+
47
+ private
48
+
49
+ def get_path(doc, attr_name, default_path, asset_to_include)
50
+ doc.normalize_web_path asset_to_include, (doc.attr attr_name, default_path), false
51
+ end
52
+
53
+ def create_script_directive(doc, attr_name, default_path, asset_to_include)
54
+ %(<script src="#{get_path doc, attr_name, default_path, asset_to_include}"></script>)
55
+ end
56
+
57
+ def create_link_css_directive(doc, attr_name, default_path, asset_to_include)
58
+ %(<link rel="stylesheet" href="#{get_path doc, attr_name, default_path, asset_to_include}">)
59
+ end
60
+
61
+ def chartjs(doc)
62
+ create_script_directive(doc, CHARTJS_DIR_ATTR, CHARTJS_DEFAULT_PATH, 'chart.min.js')
63
+ end
64
+
65
+ def chartist(doc)
66
+ result = []
67
+ result << create_link_css_directive(doc, CHARTIST_DIR_ATTR, CHARTIST_DEFAULT_PATH, 'chartist.min.css')
68
+ result << create_script_directive(doc, CHARTIST_DIR_ATTR, CHARTIST_DEFAULT_PATH, 'chartist.min.js')
69
+ result
70
+ end
71
+
72
+ def c3js(doc)
73
+ result = []
74
+ result << create_link_css_directive(doc, C3JS_DIR_ATTR, C3JS_DEFAULT_PATH, 'c3.min.css')
75
+ result << create_script_directive(doc, D3JS_DIR_ATTR, D3JS_DEFAULT_PATH, 'd3.min.js')
76
+ result << create_script_directive(doc, C3JS_DIR_ATTR, C3JS_DEFAULT_PATH, 'c3.min.js')
77
+ result
29
78
  end
30
79
  end
31
80
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor
4
+ module Chart
5
+ module Html5ChartConverterExt
6
+ def convert_chart node
7
+ chart_engine = node.attr 'engine'
8
+ chart_type = node.attr 'type'
9
+ chart_converter = Asciidoctor::Chart::Registry.for chart_engine
10
+
11
+ if chart_converter
12
+ if chart_converter.handles? chart_type
13
+ chart_converter.send "convert_#{chart_type}_chart", node
14
+ else
15
+ logger.warn %(missing chart convert handler for type '#{chart_type}' in #{chart_converter})
16
+ nil
17
+ end
18
+ else
19
+ logger.warn %(missing chart convert for engine '#{chart_engine}')
20
+ nil
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor
4
+ module Chart
5
+ class Preprocessor < Asciidoctor::Extensions::Preprocessor
6
+ def process document, reader
7
+ document.extend(ChartBlockTracker)
8
+
9
+ reader
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor
4
+ module Chart
5
+ module Registry
6
+ @registry = {}
7
+
8
+ def self.register converter, engine
9
+ @registry[engine] = converter
10
+ end
11
+
12
+ def self.for engine
13
+ @registry[engine]
14
+ end
15
+ end
16
+ end
17
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Asciidoctor
4
4
  module Chart
5
- VERSION = '1.0.0.alpha.1'
5
+ VERSION = '1.0.0'
6
6
  end
7
7
  end
@@ -1,19 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'asciidoctor/extensions'
4
+ require 'tilt'
5
+ require_relative 'chart/html5_chart_converter_ext'
6
+ require_relative 'chart/registry'
7
+ require_relative 'chart/preprocessor'
4
8
  require_relative 'chart/block_macro_processor'
5
9
  require_relative 'chart/block_processor'
6
10
  require_relative 'chart/docinfo_processor'
7
- require_relative 'chart/backend'
11
+ require_relative 'chart/chart_block'
8
12
  require_relative 'chart/plain_ruby_csv'
9
13
  require_relative 'chart/plain_ruby_random'
10
- require_relative 'chart/c3js/chart_builder'
11
- require_relative 'chart/chartjs/chart_builder'
12
- require_relative 'chart/chartist/chart_builder'
14
+ require_relative 'chart/converter'
15
+ require_relative 'chart/converter/c3js'
16
+ require_relative 'chart/converter/chartist'
17
+ require_relative 'chart/converter/chartjs'
18
+
19
+ CHART_HTML_TEMPLATE = Tilt.new('chart.html.erb', format: :html5, pretty: true, disable_escape: true) do |_t|
20
+ <<-ERB
21
+ <%= Class.new.extend(Asciidoctor::Chart::Html5ChartConverterExt).convert_chart self %>
22
+ ERB
23
+ end
24
+
25
+ # providers
26
+ Asciidoctor::Chart::Registry.register Asciidoctor::Chart::Converter::Html5ChartjsConverter.new, 'chartjs'
27
+ Asciidoctor::Chart::Registry.register Asciidoctor::Chart::Converter::Html5ChartistConverter.new, 'chartist'
28
+ Asciidoctor::Chart::Registry.register Asciidoctor::Chart::Converter::Html5C3jsConverter.new, 'c3js'
29
+
30
+ def register_chart_converter converter
31
+ if (converter.instance_of? Asciidoctor::Converter::TemplateConverter) || (converter.respond_to? 'register')
32
+ # Template based converter
33
+ converter.register 'chart', CHART_HTML_TEMPLATE
34
+ else
35
+ converter.extend(Asciidoctor::Chart::Html5ChartConverterExt)
36
+ end
37
+ end
13
38
 
14
39
  Asciidoctor::Extensions.register do
15
40
  return unless document.basebackend? 'html'
16
41
 
42
+ converter = document.converter
43
+ if converter.instance_of? Asciidoctor::Converter::CompositeConverter
44
+ register_chart_converter(converter.converters[0])
45
+ else
46
+ register_chart_converter(converter)
47
+ end
48
+ preprocessor Asciidoctor::Chart::Preprocessor
17
49
  block_macro Asciidoctor::Chart::BlockMacroProcessor
18
50
  block Asciidoctor::Chart::BlockProcessor
19
51
  docinfo_processor Asciidoctor::Chart::DocinfoProcessor
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asciidoctor-chart
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.alpha.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guillaume Grossetie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-15 00:00:00.000000000 Z
11
+ date: 2022-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: tilt
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.0.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -52,35 +66,42 @@ dependencies:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
68
  version: 3.9.0
55
- description: A set of Asciidoctor extensions that add a chart block and block macro
56
- to AsciiDoc for including charts in your AsciiDoc document.
69
+ description: |-
70
+ A set of Asciidoctor extensions that add a chart block and block macro to AsciiDoc
71
+ for including charts in your AsciiDoc document.
57
72
  email: ggrossetie@gmail.com
58
73
  executables: []
59
74
  extensions: []
60
75
  extra_rdoc_files: []
61
76
  files:
77
+ - CHANGELOG.adoc
62
78
  - LICENSE.adoc
63
79
  - README.adoc
64
80
  - asciidoctor-chart.gemspec
65
81
  - lib/asciidoctor-chart.rb
66
82
  - lib/asciidoctor/chart.rb
67
- - lib/asciidoctor/chart/backend.rb
68
83
  - lib/asciidoctor/chart/block_macro_processor.rb
69
84
  - lib/asciidoctor/chart/block_processor.rb
70
- - lib/asciidoctor/chart/c3js/chart_builder.rb
71
- - lib/asciidoctor/chart/chartist/chart_builder.rb
72
- - lib/asciidoctor/chart/chartjs/chart_builder.rb
85
+ - lib/asciidoctor/chart/chart_block.rb
86
+ - lib/asciidoctor/chart/converter.rb
87
+ - lib/asciidoctor/chart/converter/c3js.rb
88
+ - lib/asciidoctor/chart/converter/chartist.rb
89
+ - lib/asciidoctor/chart/converter/chartjs.rb
73
90
  - lib/asciidoctor/chart/docinfo_processor.rb
91
+ - lib/asciidoctor/chart/html5_chart_converter_ext.rb
74
92
  - lib/asciidoctor/chart/plain_ruby_csv.rb
75
93
  - lib/asciidoctor/chart/plain_ruby_random.rb
94
+ - lib/asciidoctor/chart/preprocessor.rb
95
+ - lib/asciidoctor/chart/registry.rb
76
96
  - lib/asciidoctor/chart/version.rb
77
97
  homepage: https://asciidoctor.org
78
98
  licenses:
79
99
  - MIT
80
100
  metadata:
81
101
  bug_tracker_uri: https://github.com/asciidoctor/asciidoctor-chart/issues
82
- mailing_list_uri: http://discuss.asciidoctor.org
102
+ community_chat_uri: https://asciidoctor.zulipchat.com
83
103
  source_code_uri: https://github.com/asciidoctor/asciidoctor-chart
104
+ rubygems_mfa_required: 'true'
84
105
  post_install_message:
85
106
  rdoc_options: []
86
107
  require_paths:
@@ -92,11 +113,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
92
113
  version: '0'
93
114
  required_rubygems_version: !ruby/object:Gem::Requirement
94
115
  requirements:
95
- - - ">"
116
+ - - ">="
96
117
  - !ruby/object:Gem::Version
97
- version: 1.3.1
118
+ version: '0'
98
119
  requirements: []
99
- rubygems_version: 3.0.3
120
+ rubygems_version: 3.0.3.1
100
121
  signing_key:
101
122
  specification_version: 4
102
123
  summary: Adds a chart block and block macro to AsciiDoc
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Asciidoctor
4
- module Chart
5
- class Backend
6
- def self.resolve_engine attrs, document
7
- if attrs.key? 'engine'
8
- attrs['engine'].downcase
9
- elsif document.attributes.key? 'chart-engine'
10
- document.attributes['chart-engine'].downcase
11
- else
12
- 'c3js'
13
- end
14
- end
15
-
16
- def self.process engine, attrs, raw_data
17
- # TODO: Check that the engine can process the required type (bar, line, step...)
18
- type = attrs['type']
19
- case engine
20
- when 'c3js'
21
- if type == 'pie'
22
- C3js::ChartBuilder.pie raw_data, attrs
23
- else
24
- data, labels = C3js::ChartBuilder.prepare_data raw_data
25
- case type
26
- when 'bar'
27
- C3js::ChartBuilder.bar data, labels, attrs
28
- when 'line'
29
- C3js::ChartBuilder.line data, labels, attrs
30
- when 'step'
31
- C3js::ChartBuilder.step data, labels, attrs
32
- when 'spline'
33
- C3js::ChartBuilder.spline data, labels, attrs
34
- else
35
- # By default chart line
36
- C3js::ChartBuilder.line data, labels, attrs
37
- end
38
- end
39
- when 'chartist'
40
- data, labels = Chartist::ChartBuilder.prepare_data raw_data
41
- case type
42
- when 'bar'
43
- Chartist::ChartBuilder.bar data, labels, attrs
44
- when 'line'
45
- Chartist::ChartBuilder.line data, labels, attrs
46
- else
47
- # By default chart line
48
- Chartist::ChartBuilder.line data, labels, attrs
49
- end
50
- when 'chartjs'
51
- data, labels = Chartjs::ChartBuilder.prepare_data raw_data
52
- # By default chart line
53
- Chartjs::ChartBuilder.line data, labels, attrs
54
- end
55
- end
56
- end
57
- end
58
- end