asciidoctor-chart 1.0.0.alpha.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f04e36b08cd8bcd45da1387e52efb5f982f92aceb1b14912130158eb4c7f34a1
4
- data.tar.gz: fc325aa365a1b8ccf11fcc3e10cb6efd52fb0a447280e72c37d8db77eb412f94
3
+ metadata.gz: d19c56f42982d8d5b332f7e3f9daa2456a46e892e19c34c474b26d916ac9c06f
4
+ data.tar.gz: 5f9c0366c859481c33078b6b6bb7ec66b311a5ed0a7b3ff816a464869d6bc35b
5
5
  SHA512:
6
- metadata.gz: dba17ce4d883a862ad885833dbe8785fbc9d3d334a35fc97a31fb0d05bf11442a2cb086cd80a23a43b15be9031d6e96dce0f48e5042c035f365dd0b1a7102d5a
7
- data.tar.gz: d55676125f54e5898e966fe0f4241c01dc24bc7bb8cfdf0a8f72601eba1999e8f69080d61efbe19dac9b7a966e658609162f9cf1fdeeede3882aae9e68b3e35b
6
+ metadata.gz: 2de58fded66f3709cfa02645bc8d0aff0b5199555f66c3108d624b5990e207dca2a3cdf49e1065a3875349df84988b75084c69ddfc2e1c60df3d4449583e7da2
7
+ data.tar.gz: 3cf74600a24dc2ae35d3b13cc7a0ea5ea1e9c64a9c44ceef9ae6ec9bcabcbd77794968b0e49f471554477cc4247b4a2e76f7f639d4b36c8932880595ad69d928
data/CHANGELOG.adoc ADDED
@@ -0,0 +1,38 @@
1
+ = Asciidoctor Chart Changelog
2
+ :icons: font
3
+ :uri-repo: https://github.com/asciidoctor/asciidoctor-chart
4
+
5
+ This document provides a high-level view of the changes introduced in Asciidoctor Chart by release.
6
+ For an even more detailed look at what has changed, refer to the {uri-repo}/commits/[commit history] on GitHub.
7
+
8
+ The format is based on https://keepachangelog.com/en/1.0.0/[Keep a Changelog],
9
+ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Versioning].
10
+
11
+ == Unreleased
12
+
13
+
14
+ == 1.0.0 (2022-10-08)
15
+
16
+ === ✨ Added
17
+
18
+ * Introduce a changelog (#30)
19
+ * Add support for using the JavaScript libraries offline (#25)
20
+ * Add captioned title on charts (#18)
21
+ * Improved documentation
22
+ * Add attributes to define chart resources directory (#26)
23
+ * Introduce visual regression tests (#34)
24
+
25
+ === 🎨 Changed
26
+
27
+ * Use 0.11.x as default version for Chartist.js instead of latest (#28)
28
+ * Update c3.js defaults to latest version 0.7.20 (#32)
29
+ * Upgrade Chart.js from 1.0.2 to 3.7.0 and update code accordingly (#17)
30
+ * Use https protocol (instead of http) when loading scripts from cdnjs.cloudflare.com (#17)
31
+ * Apply RuboCop to asciidoctor-chart.gemspec (#17)
32
+ * Enable multi-factor authentication when publishing to https://rubygems.org/ (#17)
33
+
34
+ === 🐞 Fixed
35
+
36
+ * Fix an issue to allow more than one Chart.js graphs per page (#13)
37
+ * Include assets (styles, scripts) when an chart engine is used (#24)
38
+ * Fix an issue with Chart.js where text was overlapping the graph when width was specified (#17)
data/README.adoc CHANGED
@@ -1,15 +1,101 @@
1
1
  = Asciidoctor Chart
2
+ // Aliases:
3
+ :project-name: Asciidoctor Chart
4
+ :project-handle: asciidoctor-chart
5
+ // Variables:
6
+ :release-version: 1.0.0.alpha.1
7
+ :uri-repo: https://github.com/asciidoctor/asciidoctor-chart
2
8
  // Settings:
3
9
  :idprefix:
4
10
  :idseparator: -
5
11
 
6
- image:https://github.com/asciidoctor/asciidoctor-chart/workflows/Ruby/badge.svg[link=https://github.com/asciidoctor/asciidoctor-chart/actions?query=workflow%3ARuby]
12
+ image:https://github.com/asciidoctor/asciidoctor-chart/workflows/CI/badge.svg[link=https://github.com/asciidoctor/asciidoctor-chart/actions?query=workflow%3ACI]
13
+ image:https://img.shields.io/gem/v/asciidoctor-chart?include_prereleases[link=https://rubygems.org/search?query=asciidoctor-chart]
14
+
15
+ A set of Asciidoctor extensions that adds a chart block and block macro for including charts powered by https://c3js.org/[C3.js], https://gionkunz.github.io/chartist-js/[Chartist], or https://www.chartjs.org/[Chart.js] in your AsciiDoc document.
16
+
17
+ == Prerequisites
18
+
19
+ All that's needed is Ruby 2.5 or better (or JRuby 9.2 or better) and a few Ruby gems (including at least Asciidoctor 2.0.0), which we explain how to install in the next section.
20
+
21
+ To check if you have Ruby available, use the `ruby` command to query the version installed:
22
+
23
+ $ ruby -e 'puts RUBY_VERSION'
24
+
25
+ Make sure this command reports a Ruby version that's at least 2.5.
26
+ If so, you may proceed.
27
+
28
+ == Getting Started
29
+
30
+ You can get {project-name} by <<install-the-published-gem,installing the published gem>>.
31
+ ifndef::env-site[You can also <<development,run the code from source>> if you want to use a development version or participate in development.]
32
+
33
+ === Install the Published Gem
34
+
35
+ To install {project-name}, first make sure you have satisfied the <<Prerequisites,prerequisites>>.
36
+ Then, install the gem from RubyGems.org using the following command:
37
+
38
+ $ gem install asciidoctor-chart --pre
39
+
40
+ === Enable the Extension
41
+
42
+ Assuming all the required gems install properly, you can enable the extension using `--require` option (or `-r` for short) from the Asciidoctor CLI:
43
+
44
+ $ asciidoctor --require asciidoctor-chart my-doc.adoc
45
+
46
+ === Usage
47
+
48
+ Line chart powered by C3.js (default) declared as a literal block::
49
+ +
50
+ ----
51
+ [chart,line]
52
+ ....
53
+ January,February,March,April,May,June,July
54
+ 28,48,40,19,86,27,90
55
+ 65,59,80,81,56,55,40
56
+ ....
57
+ ----
58
+ +
59
+ image::./examples/chart-c3js.png[]
60
+
61
+ Line chart powered by Chart.js declared as a block macro with a CSV file as target::
62
+ +
63
+ ----
64
+ chart::sample-data.csv[line,engine="chartjs"]
65
+ ----
66
+ +
67
+ image::./examples/chart-chartjs.png[]
68
+
69
+ For more examples, see {uri-repo}/blob/master/examples/example.adoc[example.adoc].
70
+
71
+ === Configuration
72
+
73
+ [cols="1s,1,3"]
74
+ |===
75
+ |Attribute{nbsp}Name |Value(s)|Description
76
+
77
+ |c3jsdir
78
+ |<file\|URL>
79
+ |Overrides c3.js directory, where the following files `c3.min.css` and `c3.min.js` are expected. Default is `https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.20/`.
80
+
81
+ |chartjsdir
82
+ |<file\|URL>
83
+ |Overrides chart.js directory, where the following file `chart.min.js` is expected. Default is `https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/`.
84
+
85
+ |chartistdir
86
+ |<file\|URL>
87
+ |Overrides chartist.js directory, where the following files `chartist.min.css` and `chartist.min.js` are expected. Default is `https://cdn.jsdelivr.net/chartist.js/0.11.x/`.
88
+
89
+
90
+ |d3jsdir
91
+ |<file\|URL>
92
+ |Overrides d3.js directory, where the following file `d3.min.js` is expected. Default is `https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/`.
93
+ |===
7
94
 
8
- A set of Asciidoctor extensions that adds a chart block and block macro for including charts powered by powered by c3js, chartist, or chartjs in your AsciiDoc document.
9
95
 
10
96
  == Authors
11
97
 
12
- Asciidoctor Chart was written by https://github.com/mogztter/[Guillaume Grossetie].
98
+ {project-name} was written by https://github.com/mogztter/[Guillaume Grossetie].
13
99
 
14
100
  == Copyright
15
101
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require_relative 'lib/asciidoctor/chart/version'
3
5
  rescue LoadError
@@ -8,31 +10,36 @@ Gem::Specification.new do |s|
8
10
  s.name = 'asciidoctor-chart'
9
11
  s.version = Asciidoctor::Chart::VERSION
10
12
  s.summary = 'Adds a chart block and block macro to AsciiDoc'
11
- s.description = 'A set of Asciidoctor extensions that add a chart block and block macro to AsciiDoc for including charts in your AsciiDoc document.'
13
+ s.description = "A set of Asciidoctor extensions that add a chart block and block macro to AsciiDoc
14
+ for including charts in your AsciiDoc document."
12
15
  s.authors = ['Guillaume Grossetie']
13
16
  s.email = 'ggrossetie@gmail.com'
14
17
  s.homepage = 'https://asciidoctor.org'
15
18
  s.license = 'MIT'
16
- # NOTE required ruby version is informational only; it's not enforced since it can't be overridden and can cause builds to break
17
- #s.required_ruby_version = '>= 2.5.0'
19
+ # NOTE: required ruby version is informational only;
20
+ # it's not enforced since it can't be overridden and can cause builds to break
21
+ # s.required_ruby_version = '>= 2.7.0'
18
22
  s.metadata = {
19
23
  'bug_tracker_uri' => 'https://github.com/asciidoctor/asciidoctor-chart/issues',
20
- #'changelog_uri' => 'https://github.com/asciidoctor/asciidoctor-chart/blob/master/CHANGELOG.adoc',
21
- 'mailing_list_uri' => 'http://discuss.asciidoctor.org',
22
- 'source_code_uri' => 'https://github.com/asciidoctor/asciidoctor-chart'
24
+ # 'changelog_uri' => 'https://github.com/asciidoctor/asciidoctor-chart/blob/master/CHANGELOG.adoc',
25
+ 'community_chat_uri' => 'https://asciidoctor.zulipchat.com',
26
+ 'source_code_uri' => 'https://github.com/asciidoctor/asciidoctor-chart',
27
+ 'rubygems_mfa_required' => 'true'
23
28
  }
24
29
 
25
- # NOTE the logic to build the list of files is designed to produce a usable package even when the git command is not available
30
+ # NOTE: the logic to build the list of files is designed to produce a usable package
31
+ # even when the git command is not available
26
32
  begin
27
- files = (result = `git ls-files -z`.split ?\0).empty? ? Dir['**/*'] : result
28
- rescue
33
+ files = (result = `git ls-files -z`.split "\0").empty? ? Dir['**/*'] : result
34
+ rescue StandardError
29
35
  files = Dir['**/*']
30
36
  end
31
- s.files = files.grep %r/^(?:(?:data|lib)\/.+|(?:CHANGELOG|LICENSE|NOTICE|README)\.adoc|\.yardopts|#{s.name}\.gemspec)$/
32
- s.executables = (files.grep %r/^bin\//).map {|f| File.basename f }
37
+ s.files = files.grep %r{^(?:(?:data|lib)/.+|(?:CHANGELOG|LICENSE|NOTICE|README)\.adoc|\.yardopts|#{s.name}\.gemspec)$}
38
+ s.executables = (files.grep %r{^bin/}).map {|f| File.basename f }
33
39
  s.require_paths = ['lib']
34
40
 
35
41
  s.add_runtime_dependency 'asciidoctor', '~> 2.0'
42
+ s.add_runtime_dependency 'tilt', '~> 2.0.0'
36
43
  s.add_development_dependency 'rake', '~> 13.0.0'
37
44
  s.add_development_dependency 'rspec', '~> 3.9.0'
38
45
  end
@@ -12,10 +12,8 @@ module Asciidoctor
12
12
  read_data = parent.read_asset data_path, warn_on_failure: true, normalize: true
13
13
  return if read_data.nil? || read_data.empty?
14
14
 
15
- engine = Backend.resolve_engine attrs, parent.document
16
15
  raw_data = PlainRubyCSV.parse read_data
17
- html = Backend.process engine, attrs, raw_data
18
- create_pass_block parent, html, attrs, subs: nil
16
+ Asciidoctor::Chart::ChartBlock.new parent, raw_data, attrs
19
17
  end
20
18
  end
21
19
  end
@@ -10,10 +10,8 @@ module Asciidoctor
10
10
  parse_content_as :raw
11
11
 
12
12
  def process parent, reader, attrs
13
- engine = Backend.resolve_engine attrs, parent.document
14
13
  raw_data = PlainRubyCSV.parse reader.source
15
- html = Backend.process engine, attrs, raw_data
16
- create_pass_block parent, html, attrs, subs: nil
14
+ Asciidoctor::Chart::ChartBlock.new parent, raw_data, attrs
17
15
  end
18
16
  end
19
17
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor
4
+ module Chart
5
+ class ChartBlock < Asciidoctor::Block
6
+ def initialize(parent, data, attrs)
7
+ engine = parent.document.get_engine attrs
8
+ caption = attrs.delete 'caption'
9
+ title = attrs.delete 'title'
10
+ block_attributes = attrs.merge({
11
+ 'id' => attrs['id'] || "chart#{PlainRubyRandom.uuid}",
12
+ 'type' => attrs['type'] || 'line',
13
+ 'engine' => engine,
14
+ 'data-raw' => data
15
+ })
16
+ super parent, :chart, { source: nil, attributes: block_attributes, subs: nil }
17
+ @title = title
18
+ assign_caption(caption, 'figure')
19
+ end
20
+ end
21
+
22
+ module ChartBlockTracker
23
+ attr_reader :x_chart
24
+
25
+ def self.extended instance
26
+ instance.instance_variable_set :@x_chart, {
27
+ engines: Set.new
28
+ }
29
+ end
30
+
31
+ def get_engine attrs
32
+ engine = if attrs.key? 'engine'
33
+ attrs['engine'].downcase
34
+ elsif @attributes.key? 'chart-engine'
35
+ @attributes['chart-engine'].downcase
36
+ else
37
+ 'c3js'
38
+ end
39
+ @x_chart[:engines].add(engine)
40
+
41
+ engine
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,231 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor
4
+ module Chart
5
+ module Converter
6
+ class Html5C3jsConverter < Asciidoctor::Chart::Converter::Base
7
+ def convert_line_chart node
8
+ data, labels = prepare_data node
9
+ chart_generate_script = chart_line_script node, data, labels
10
+ <<~HTML
11
+ #{chart_block_element node}
12
+ #{chart_generate_script}
13
+ HTML
14
+ end
15
+
16
+ def convert_bar_chart node
17
+ data, labels = prepare_data node
18
+ <<~HTML
19
+ #{chart_block_element node}
20
+ #{chart_bar_script node, data, labels}
21
+ HTML
22
+ end
23
+
24
+ def convert_step_chart node
25
+ data, labels = prepare_data node
26
+ <<~HTML
27
+ #{chart_block_element node}
28
+ #{chart_step_script node, data, labels}
29
+ HTML
30
+ end
31
+
32
+ def convert_spline_chart node
33
+ data, labels = prepare_data node
34
+ <<~HTML
35
+ #{chart_block_element node}
36
+ #{chart_spline_script node, data, labels}
37
+ HTML
38
+ end
39
+
40
+ def convert_pie_chart node
41
+ raw_data = node.attr 'data-raw', []
42
+ <<~HTML
43
+ #{chart_block_element node}
44
+ #{chart_pie_script node, raw_data}
45
+ HTML
46
+ end
47
+
48
+ private
49
+
50
+ def prepare_data node
51
+ raw_data = node.attr 'data-raw', []
52
+ return [[], []] if raw_data.length <= 1 # question: should we warn?
53
+
54
+ labels = raw_data[0]
55
+ raw_data.shift
56
+ raw_data.map.with_index {|row, index| row.unshift index.to_s }
57
+ [raw_data, labels]
58
+ end
59
+
60
+ def chart_bar_script node, data, labels
61
+ chart_height = get_chart_height node
62
+ chart_width = get_chart_width node
63
+ axis_x_label = get_axis_x_label node
64
+ axis_y_label = get_axis_y_label node
65
+ data_names = get_data_names node
66
+ <<~HTML
67
+ <script>
68
+ c3.generate({
69
+ bindto: '##{node.attr 'id'}',
70
+ size: { height: #{chart_height}, width: #{chart_width} },
71
+ data: {
72
+ columns: #{data.to_s},
73
+ type: 'bar',
74
+ names: #{data_names.to_s}
75
+ },
76
+ axis: {
77
+ x: {
78
+ type: 'category',
79
+ categories: #{labels.to_s},
80
+ label: '#{axis_x_label}'
81
+ },
82
+ y: {
83
+ label: '#{axis_y_label}'
84
+ }
85
+ }
86
+ })
87
+ </script>
88
+ HTML
89
+ end
90
+
91
+ def chart_line_script node, data, labels
92
+ chart_height = get_chart_height node
93
+ chart_width = get_chart_width node
94
+ axis_x_label = get_axis_x_label node
95
+ axis_y_label = get_axis_y_label node
96
+ data_names = get_data_names node
97
+ <<~HTML
98
+ <script>
99
+ c3.generate({
100
+ bindto: '##{node.attr 'id'}',
101
+ size: { height: #{chart_height}, width: #{chart_width} },
102
+ data: {
103
+ columns: #{data.to_s},
104
+ names: #{data_names.to_s}
105
+ },
106
+ axis: {
107
+ x: {
108
+ type: 'category',
109
+ categories: #{labels.to_s},
110
+ label: '#{axis_x_label}'
111
+ },
112
+ y: {
113
+ label: '#{axis_y_label}'
114
+ }
115
+ }
116
+ })
117
+ </script>
118
+ HTML
119
+ end
120
+
121
+ def chart_step_script node, data, labels
122
+ chart_height = get_chart_height node
123
+ chart_width = get_chart_width node
124
+ axis_x_label = get_axis_x_label node
125
+ axis_y_label = get_axis_y_label node
126
+ data_names = get_data_names node
127
+ <<~HTML
128
+ <script>
129
+ c3.generate({
130
+ bindto: '##{node.attr 'id'}',
131
+ size: { height: #{chart_height}, width: #{chart_width} },
132
+ data: {
133
+ columns: #{data.to_s},
134
+ type: 'step',
135
+ names: #{data_names.to_s}
136
+ },
137
+ axis: {
138
+ x: {
139
+ type: 'category',
140
+ categories: #{labels.to_s},
141
+ label: '#{axis_x_label}'
142
+ },
143
+ y: {
144
+ label: '#{axis_y_label}'
145
+ }
146
+ }
147
+ })
148
+ </script>
149
+ HTML
150
+ end
151
+
152
+ def chart_spline_script node, data, labels
153
+ chart_height = get_chart_height node
154
+ chart_width = get_chart_width node
155
+ axis_x_label = get_axis_x_label node
156
+ axis_y_label = get_axis_y_label node
157
+ data_names = get_data_names node
158
+ <<~HTML
159
+ <script>
160
+ c3.generate({
161
+ bindto: '##{node.attr 'id'}',
162
+ size: { height: #{chart_height}, width: #{chart_width} },
163
+ data: {
164
+ columns: #{data.to_s},
165
+ type: 'spline',
166
+ names: #{data_names.to_s}
167
+ },
168
+ axis: {
169
+ x: {
170
+ type: 'category',
171
+ categories: #{labels.to_s},
172
+ label: '#{axis_x_label}'
173
+ },
174
+ y: {
175
+ label: '#{axis_y_label}'
176
+ }
177
+ }
178
+ })
179
+ </script>
180
+ HTML
181
+ end
182
+
183
+ def chart_pie_script node, raw_data
184
+ chart_height = get_chart_height node
185
+ chart_width = get_chart_width node
186
+ <<~HTML
187
+ <script>
188
+ c3.generate({
189
+ bindto: '##{node.attr 'id'}',
190
+ size: { height: #{chart_height}, width: #{chart_width} },
191
+ data: {
192
+ columns: #{raw_data.to_s},
193
+ type: 'pie'
194
+ }
195
+ })
196
+ </script>
197
+ HTML
198
+ end
199
+
200
+ def chart_block_element node
201
+ title_element = node.title? ? %(\n <div class="title">#{node.captioned_title}</div>) : ''
202
+ %(<div class="chartblock">
203
+ <div class="content c3js-content">
204
+ <div id="#{node.attr 'id'}"></div>
205
+ </div>#{title_element}
206
+ </div>)
207
+ end
208
+
209
+ def get_chart_height node
210
+ node.attr 'height', '400'
211
+ end
212
+
213
+ def get_chart_width node
214
+ node.attr 'width', '600'
215
+ end
216
+
217
+ def get_axis_x_label node
218
+ node.attr?('axis-x-label') ? CGI.unescapeHTML(node.attr('axis-x-label')) : ''
219
+ end
220
+
221
+ def get_axis_y_label node
222
+ node.attr?('axis-y-label') ? CGI.unescapeHTML(node.attr('axis-y-label')) : ''
223
+ end
224
+
225
+ def get_data_names node
226
+ node.attr?('data-names') ? CGI.unescapeHTML(node.attr('data-names')) : '{}'
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor
4
+ module Chart
5
+ module Converter
6
+ class Html5ChartistConverter < Asciidoctor::Chart::Converter::Base
7
+ def convert_line_chart node
8
+ data, labels = prepare_data node
9
+ <<~HTML
10
+ #{chart_block_element node}
11
+ #{chart_line_script node, data, labels}
12
+ HTML
13
+ end
14
+
15
+ def convert_bar_chart node
16
+ data, labels = prepare_data node
17
+ <<~HTML
18
+ #{chart_block_element node}
19
+ #{chart_bar_script node, data, labels}
20
+ HTML
21
+ end
22
+
23
+ private
24
+
25
+ def prepare_data node
26
+ raw_data = node.attr 'data-raw', []
27
+ return [[], []] if raw_data.length <= 1 # question: should we warn?
28
+
29
+ labels = raw_data[0]
30
+ raw_data.shift
31
+ [raw_data, labels]
32
+ end
33
+
34
+ def chart_block_element node
35
+ title_element = node.title? ? %(\n <div class="title">#{node.captioned_title}</div>) : ''
36
+ %(<div class="chartblock">
37
+ <div class="content chartist-content">
38
+ <div id="#{node.attr 'id'}" class="ct-chart"></div>
39
+ </div>#{title_element}
40
+ </div>)
41
+ end
42
+
43
+ def chart_line_script node, data, labels
44
+ chart_height = node.attr 'height', '400'
45
+ chart_width = node.attr 'width', '600'
46
+ <<~HTML
47
+ <script>
48
+ var options = {
49
+ height: '#{chart_height}',
50
+ width: '#{chart_width}',
51
+ colors: ["#72B3CC", "#8EB33B"]
52
+ }
53
+ var data = {
54
+ labels: #{labels.to_s},
55
+ series: #{data.to_s}
56
+ }
57
+ new Chartist.Line('##{node.attr 'id'}', data, options)
58
+ </script>
59
+ HTML
60
+ end
61
+
62
+ def chart_bar_script node, data, labels
63
+ chart_height = node.attr 'height', '400'
64
+ <<~HTML
65
+ <script>
66
+ var options = {
67
+ height: '#{chart_height}',
68
+ colors: ["#72B3CC", "#8EB33B"]
69
+ }
70
+ var data = {
71
+ labels: #{labels.to_s},
72
+ series: #{data.to_s}
73
+ }
74
+ new Chartist.Bar('##{node.attr 'id'}', data, options)
75
+ </script>
76
+ HTML
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end