nora_mark 0.2beta7 → 0.2beta8

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.
@@ -1,74 +1,12 @@
1
1
  require "nora_mark/version"
2
- require 'nora_mark/html/generator'
3
- require 'nora_mark/parser'
4
2
  require 'nora_mark/node_util'
5
- require 'nora_mark/node'
6
- require 'nora_mark/node_set'
7
3
  require 'nora_mark/transformer'
8
- require 'nora_mark/node_builder'
9
- require 'securerandom'
10
-
11
- module NoraMark
12
- class Document
13
- attr_accessor :document_name, :root
14
- private_class_method :new
15
-
16
- def self.parse(string_or_io, param = {})
17
- instance = new param
18
- src = (string_or_io.respond_to?(:read) ? string_or_io.read : string_or_io).encode 'utf-8'
19
- yield instance if block_given?
20
- instance.instance_eval do
21
- @preprocessors.each do
22
- |pr|
23
- src = pr.call(src)
24
- end
25
- parser = Parser.new(src)
26
- if (!parser.parse)
27
- raise parser.raise_error
28
- end
29
- @root = parser.result
30
- @root.document_name ||= @document_name
31
- @root.reparent
32
- @root.first_child.inject(1) do |page_no, node|
33
- if node.kind_of? Page
34
- node.page_no = page_no
35
- page_no = page_no + 1
36
- end
37
- page_no
38
- end
39
- end
40
- instance
41
- end
4
+ require 'nora_mark/parser'
5
+ require 'nora_mark/node_set'
6
+ require 'nora_mark/html/generator'
7
+ require 'nora_mark/document'
8
+ require 'nora_mark/extensions'
42
9
 
43
- def preprocessor(&block)
44
- @preprocessors << block
45
- end
46
10
 
47
- def html
48
- if @html.nil?
49
- @transformers[:html].each { |t| t.transform @root }
50
- @html = Html::Generator.new(@param).convert(@root.clone, @render_parameter)
51
- end
52
- @html
53
- end
54
11
 
55
- def render_parameter(param = {})
56
- @render_parameter.merge! param
57
- self
58
- end
59
12
 
60
- def add_transformer(generator: :html, text: nil, &block)
61
- (@transformers[generator] ||= []) << TransformerFactory.create(text: text, &block)
62
- end
63
-
64
- def initialize(param = {})
65
- @param = param
66
- @preprocessors = [
67
- Proc.new { |text| text.gsub(/\r?\n(\r?\n)+/, "\n\n") },
68
- ]
69
- @document_name = param[:document_name] || "noramark_#{SecureRandom.uuid}"
70
- @render_parameter = {}
71
- @transformers = { html: []}
72
- end
73
- end
74
- end
@@ -0,0 +1,89 @@
1
+ require 'securerandom'
2
+
3
+ module NoraMark
4
+ class Document
5
+ attr_accessor :document_name, :root
6
+ private_class_method :new
7
+
8
+ def self.generators
9
+ @generators ||= {}
10
+ end
11
+
12
+ def self.register_generator(generator)
13
+ @generators ||= {}
14
+ generator_name = generator.name
15
+ @generators[generator_name] = generator
16
+ generator.activate self if generator.respond_to? :activate
17
+ class_eval do
18
+ define_method(generator_name) do
19
+ generate(generator_name)
20
+ end
21
+ end
22
+ end
23
+
24
+ register_generator(::NoraMark::Html::Generator)
25
+
26
+ def self.parse(string_or_io, param = {})
27
+ instance = new param
28
+ src = (string_or_io.respond_to?(:read) ? string_or_io.read : string_or_io).encode 'utf-8'
29
+ yield instance if block_given?
30
+ instance.instance_eval do
31
+ @preprocessors.each do
32
+ |pr|
33
+ src = pr.call(src)
34
+ end
35
+ parser = Parser.new(src)
36
+ if (!parser.parse)
37
+ raise parser.raise_error
38
+ end
39
+ @root = parser.result
40
+ @root.document_name ||= @document_name
41
+ @root.reparent
42
+ @root.first_child.inject(1) do |page_no, node|
43
+ if node.kind_of? Page
44
+ node.page_no = page_no
45
+ page_no = page_no + 1
46
+ end
47
+ page_no
48
+ end
49
+ end
50
+ instance
51
+ end
52
+
53
+ def preprocessor(&block)
54
+ @preprocessors << block
55
+ end
56
+
57
+ def transformers(generator_name)
58
+ @transformers[generator_name] ||= []
59
+ end
60
+
61
+ def generate(generator_name)
62
+ if @result[generator_name].nil?
63
+ transformers(generator_name).each { |t| t.transform @root }
64
+ @result[generator_name] = Document.generators[generator_name].new(@param).convert(@root.clone, @render_parameter)
65
+ end
66
+ @result[generator_name]
67
+ end
68
+
69
+ def render_parameter(param = {})
70
+ @render_parameter.merge! param
71
+ self
72
+ end
73
+
74
+ def add_transformer(generator: :html, text: nil, &block)
75
+ (@transformers[generator] ||= []) << TransformerFactory.create(text: text, &block)
76
+ end
77
+
78
+ def initialize(param = {})
79
+ @param = param
80
+ @result = {}
81
+ @preprocessors = [
82
+ Proc.new { |text| text.gsub(/\r?\n(\r?\n)+/, "\n\n") },
83
+ ]
84
+ @document_name = param[:document_name] || "noramark_#{SecureRandom.uuid}"
85
+ @render_parameter = {}
86
+ @transformers = { }
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,26 @@
1
+ module NoraMark
2
+ class Extensions
3
+ def self.register_generator(generator)
4
+ if !generator.respond_to? :name
5
+ generator = load_generator(generator)
6
+ end
7
+ NoraMark::Document.register_generator(generator)
8
+ end
9
+
10
+ def load_generator(generator)
11
+ module_name = '::NoraMark::#{generator.to_s.capitalize}::Generator'
12
+ return const_get(module_name) if const_defined? module_name.to_sym
13
+ path = "#{generator.to_s.lowercase}.rb"
14
+ current_dir_path = File.expand_path(File.join('.', '.noramark-plugins', path))
15
+ home_dir_path = File.expand_path(File.join(ENV['HOME'], '.noramark-plugins', path))
16
+ if File.exist? current_dir_path
17
+ require current_dir_path
18
+ elsif File.exist? home_dir_path
19
+ require home_dir_path
20
+ else
21
+ require "noramark/#{generator.to_s.lowercase}"
22
+ end
23
+ const_defined?(module_name.to_sym) ? const_get(module_name) : nil
24
+ end
25
+ end
26
+ end
@@ -32,7 +32,7 @@ module NoraMark
32
32
  end
33
33
 
34
34
  def chop_last_space
35
- @pages.last.sub!(/[[:space:]]+$/, '')
35
+ @pages.last.sub!(/[[:space:]]+\Z/, '')
36
36
  end
37
37
 
38
38
  def paragraph_style=(style)
@@ -68,14 +68,13 @@ module NoraMark
68
68
  page << f.call
69
69
  }
70
70
  page << "</head>\n"
71
- page << "<body>\n"
72
71
  @pages << page
73
72
  end
74
73
 
75
74
  def end_html
75
+ return if @pages.size == 0
76
76
  page = @pages.last
77
77
  if !page.frozen?
78
- page << "</body>\n"
79
78
  page << "</html>\n"
80
79
  page.freeze
81
80
  end
@@ -0,0 +1,151 @@
1
+ module NoraMark
2
+ module Html
3
+ DEFAULT_TRANSFORMER = TransformerFactory.create do
4
+ rename 'd', 'div'
5
+ rename 'art', 'article'
6
+ rename 'arti', 'article'
7
+ rename 'sec', 'section'
8
+ rename 'sect', 'section'
9
+ rename 'sp', 'span'
10
+
11
+ modify({type: :Root}) do
12
+ if (@options[:render_parameter] ||= {})[:nonpaged]
13
+ first_page = @node.first_child.remove
14
+ @node.children.each do |node|
15
+ if node.class == NoraMark::Page
16
+ first_page.append_child block('hr', classes: ['page-break'], body_empty: true)
17
+ node.remove
18
+ node.children.each do |cn|
19
+ first_page.append_child cn
20
+ end
21
+ else
22
+ first_page.append_child node
23
+ end
24
+ end
25
+ @node.prepend_child first_page
26
+ end
27
+ end
28
+ modify(/\A(l|link)\Z/) do
29
+ @node.name = 'a'
30
+ (@node.attrs ||= {}).merge!({href: [@node.params[0].text]})
31
+ end
32
+
33
+ modify 'ruby' do
34
+ @node.append_child inline 'rp', '('
35
+ @node.append_child inline 'rt', escape_html(@node.params[0].text.strip)
36
+ @node.append_child inline 'rp', ')'
37
+ end
38
+
39
+ modify 'tcy' do
40
+ @node.name = 'span'
41
+ @node.classes = ['tcy']
42
+ end
43
+
44
+ modify 'img' do
45
+ @node.body_empty = true
46
+ (@node.attrs ||= {}).merge!({src: [@node.params[0].text ]})
47
+ @node.attrs.merge!({alt: [ escape_html(@node.params[1].text.strip)]}) if (@node.p.size > 1 && @node.params[1].text.size > 0)
48
+ end
49
+
50
+ replace 'image' do
51
+ newnode = block('figure',
52
+ class_if_empty:'img-wrap',
53
+ ids: @node.ids,
54
+ children: [ inline('img', nil, attrs: {src: [ @node.params[0].text.strip], alt: [ (@node.params[1].text ||'').strip ] }) ],
55
+ template: @node)
56
+ if !@node.children_empty?
57
+ if @node.n[:caption_before]
58
+ newnode.prepend_child inline('figcaption', @node.children)
59
+ else
60
+ newnode.append_child inline('figcaption', @node.children)
61
+ end
62
+ end
63
+ newnode
64
+ end
65
+
66
+ replace({type: :OrderedList}) do
67
+ block('ol', template: @node)
68
+ end
69
+
70
+ replace({type: :UnorderedList}) do
71
+ block('ul', template: @node)
72
+ end
73
+
74
+ replace({type: :UlItem}) do
75
+ block('li', template: @node)
76
+ end
77
+
78
+ replace({type: :OlItem}) do
79
+ block('li', template: @node)
80
+ end
81
+
82
+ replace({type: :DefinitionList}) do
83
+ block('dl', template: @node)
84
+ end
85
+
86
+ replace({type: :DLItem}) do
87
+ [
88
+ block('dt', @node.p[0], n: {chop_last_space: true}),
89
+ block('dd', @node.children)
90
+ ]
91
+ end
92
+
93
+ replace({type: :Breakline}) do
94
+ newnode = block('br')
95
+ newnode.body_empty = true
96
+ newnode
97
+ end
98
+
99
+ replace({type: :HeadedSection}) do
100
+ block('section', [ block("h#{@node.level}", @node.heading, ids: @node.n[:heading_id], n: {chop_last_space: true}) ] + @node.children, template: @node)
101
+ end
102
+
103
+ replace ({type: :CodeInline}) do
104
+ inline('code', @node.content, line_no: @node.line_no, template: @node)
105
+ end
106
+
107
+ replace ({type: :PreformattedBlock}) do
108
+ new_node = block('pre')
109
+ if @node.codelanguage
110
+ new_node.attrs = {'data-code-language' => [@node.codelanguage]}
111
+ new_node.classes = (@node.classes ||[]) << "code-#{@node.codelanguage}"
112
+ end
113
+ if @node.name == 'code'
114
+ code = block('code', text(@node.content.join("\n"), raw_text: true))
115
+ new_node.children = [ code ]
116
+ else
117
+ new_node.children = [ text(@node.content.join("\n"), raw_text: true) ]
118
+ end
119
+ if (@node.p || []).size> 0
120
+ method = @node.n[:caption_after] ? :prepend : :append
121
+ new_node = new_node.wrap block('div', classes: ['pre'], children: [ block('p', children: @node.p.shift, classes: ['caption']) ]), method
122
+ end
123
+ new_node
124
+ end
125
+
126
+ modify 'video' do
127
+ @node.attrs ||= {}
128
+ @node.attrs.merge!({src: [@node.p.shift.text]})
129
+ @node.attrs.merge!({poster: [@node.n[:poster]]}) unless @node.n[:poster].nil?
130
+
131
+ options = @node.p.map { |opt| opt.text.strip }
132
+ ['autoplay', 'controls', 'loop', 'muted'].each do
133
+ |attr|
134
+ @node.attrs.merge!({attr.to_sym => true}) if options.member? attr
135
+ end
136
+ end
137
+
138
+ modify 'audio' do
139
+ @node.attrs ||= {}
140
+ @node.attrs.merge!({src: [@node.p.shift.text]})
141
+ @node.attrs.merge!({volume: [@node.n[:volume]]}) unless @node.n[:volume].nil?
142
+ options = @node.p.map { |opt| opt.text.strip }
143
+ ['autoplay', 'controls', 'loop', 'muted'].each do
144
+ |attr|
145
+ @node.attrs.merge!({attr.to_sym => true}) if options.member? attr
146
+ end
147
+ end
148
+ end
149
+ DEFAULT_TRANSFORMER.extend Util
150
+ end
151
+ end
@@ -5,163 +5,42 @@ require 'nora_mark/html/context'
5
5
  require 'nora_mark/html/tag_writer'
6
6
  require 'nora_mark/html/frontmatter_writer'
7
7
  require 'nora_mark/html/paragraph_writer'
8
- require 'nora_mark/html/writer_selector'
9
8
  require 'nora_mark/html/abstract_node_writer'
9
+ require 'nora_mark/html/default_transformer'
10
+
10
11
  module NoraMark
11
12
  module Html
12
13
  class Generator
13
14
  include Util
14
15
  attr_reader :context
16
+ def self.name
17
+ :html
18
+ end
19
+
15
20
  def initialize(param = {})
16
21
  @context = Context.new(param)
17
- article_writer = TagWriter.create('article', self)
18
- section_writer = TagWriter.create('section', self)
19
- link_writer = TagWriter.create('a', self, trailer: '',
20
- node_preprocessor: proc do |node|
21
- (node.attrs ||= {}).merge!({href: [node.parameters[0]]})
22
- node
23
- end)
24
-
25
22
  frontmatter_writer = FrontmatterWriter.new self
26
23
  paragraph_writer = ParagraphWriter.new self
27
24
  abstract_node_writer = AbstractNodeWriter.new self
28
-
29
- newpage_writer = TagWriter.create('div', self,
30
- node_preprocessor: proc do |node|
31
- node.no_tag = true
32
- node
33
- end,
34
- write_body_preprocessor: proc do |node|
35
- title = nil
36
- if node.parameters.size > 0 && node.parameters[0].size > 0
37
- title = escape_html node.parameters.first
38
- end
39
- @context.title = title unless title.nil?
40
- @context.end_html
41
- :done
42
- end
43
- )
44
- @hr_writer = TagWriter.create('hr', self, node_preprocessor: proc do |node|
45
- node.body_empty = true
46
- add_class node, 'page-break'
47
- node
48
- end)
49
-
25
+ page_writer = TagWriter.create('body', self,
26
+ node_preprocessor: proc do |node|
27
+ @context.end_html
28
+ if node.first_child.class == Frontmatter
29
+ frontmatter = node.first_child
30
+ frontmatter_writer.write frontmatter
31
+ frontmatter.remove
32
+ end
33
+ node
34
+ end);
35
+
50
36
  @writers = {
51
37
  Paragraph => paragraph_writer,
52
38
  ParagraphGroup => paragraph_writer,
53
- Breakline =>
54
- TagWriter.create('br', self, node_preprocessor: proc do |node|
55
- node.body_empty = true
56
- node
57
- end),
58
- Block =>
59
- WriterSelector.new(self,
60
- {
61
- 'd' => TagWriter.create('div', self),
62
- 'art' => article_writer,
63
- 'arti' => article_writer,
64
- 'article' => article_writer,
65
- 'sec' => section_writer,
66
- 'sect' => section_writer,
67
- 'section' => section_writer,
68
- 'image' =>
69
- TagWriter.create('div', self,
70
- node_preprocessor: proc do |node|
71
- add_class_if_empty node, 'img-wrap'
72
- node
73
- end,
74
- write_body_preprocessor: proc do |node|
75
- src = node.parameters[0].strip
76
- alt = (node.parameters[1] || '').strip
77
- caption_before = node.named_parameters[:caption_before]
78
- if caption_before && children_not_empty(node)
79
- output "<p>"; write_children node; output "</p>"
80
- end
81
- output "<img src='#{src}' alt='#{escape_html alt}' />"
82
- if !caption_before && children_not_empty(node)
83
- output "<p>"; write_children node; output "</p>"
84
- end
85
- :done
86
- end
87
- ),
88
-
89
- }),
90
- Newpage => newpage_writer,
91
- Inline =>
92
- WriterSelector.new(self,
93
- {
94
- 'link' => link_writer,
95
- 'l' => link_writer,
96
- 's' => TagWriter.create('span', self),
97
- 'img' =>
98
- TagWriter.create('img', self,
99
- node_preprocessor: proc do |node|
100
- node.body_empty = true #TODO : it is not just an item's attribute, 'img_inline' has no body. maybe should specify in parser.{rb|kpeg}
101
- (node.attrs ||= {}).merge!({src: [node.parameters[0] ]})
102
- node.attrs.merge!({alt: [ escape_html(node.parameters[1].strip)]}) if (node.parameters.size > 1 && node.parameters[1].size > 0)
103
- node
104
- end) ,
105
- 'tcy' =>
106
- TagWriter.create('span', self,
107
- node_preprocessor: proc do |node|
108
- add_class node, 'tcy'
109
- node
110
- end),
111
- 'ruby' =>
112
- TagWriter.create('ruby', self,
113
- write_body_preprocessor: proc do |node|
114
- write_children node
115
- output "<rp>(</rp><rt>#{escape_html node.parameters[0].strip}</rt><rp>)</rp>"
116
- :done
117
- end),
118
-
119
- },
120
- trailer_default:''
121
- ),
122
- OrderedList => TagWriter.create('ol', self),
123
- UnorderedList => TagWriter.create('ul', self),
124
- UlItem => TagWriter.create('li', self),
125
- OlItem => TagWriter.create('li', self),
126
- DefinitionList => TagWriter.create('dl', self),
127
- DLItem =>
128
- TagWriter.create('', self, chop_last_space: true, node_preprocessor: proc do |node| node.no_tag = true; node end,
129
- write_body_preprocessor: proc do |node|
130
- output "<dt>"; write_nodeset node.parameters[0]; output "</dt>\n"
131
- output "<dd>"; write_children node; output "</dd>\n"
132
- :done
133
- end),
134
-
39
+ Inline =>TagWriter.create(nil, self, trailer: ''),
40
+ Block => TagWriter.create(nil, self),
135
41
  Document => abstract_node_writer,
136
- Page => abstract_node_writer,
137
-
138
- #headed-section
139
- HeadedSection =>
140
- TagWriter.create('section', self, write_body_preprocessor: proc do |node|
141
- output "<h#{node.level}#{ids_string(node.named_parameters[:heading_id])}>"
142
- write_nodeset node.heading
143
- @generator.context.chop_last_space
144
- output "</h#{node.level}>\n"
145
- :continue
146
- end),
147
- # frontmatter
42
+ Page => page_writer,
148
43
  Frontmatter => frontmatter_writer,
149
- # pre-formatted
150
- PreformattedBlock =>
151
- TagWriter.create('pre', self,
152
- node_preprocessor: proc do |node|
153
- if node.codelanguage
154
- (node.attrs ||= {}).merge!({'data-code-language' => [node.codelanguage]})
155
- add_class node, "code-#{node.codelanguage}"
156
- end
157
- node
158
- end,
159
- write_body_preprocessor: proc do |node|
160
- output "<code>" if node.name == 'code'
161
- output escape_html(node.content.join "\n")
162
- output "</code>" if node.name == 'code'
163
- :done
164
- end),
165
44
  }
166
45
  end
167
46
 
@@ -180,7 +59,7 @@ module NoraMark
180
59
  end
181
60
  @id_pool[id] = x
182
61
  end
183
- @headings << x if (x.kind_of?(Block) && x.name =~ /h[1-6]/) || x.kind_of?(HeadedSection)
62
+ @headings << x if (x.kind_of?(Block) && x.name =~ /h[1-6]/)
184
63
  end
185
64
  end
186
65
  def assign_id_to_headings
@@ -188,27 +67,24 @@ module NoraMark
188
67
  count = 1
189
68
  @headings.each do
190
69
  |heading|
191
- if heading.ids.size == 0 || heading.kind_of?(HeadedSection)
70
+ if heading.ids.size == 0
192
71
  begin
193
72
  id = "heading_index_#{count}"
194
73
  count = count + 1
195
74
  end while @id_pool[id]
196
- heading.kind_of?(HeadedSection) ? (heading.named_parameters[:heading_id] ||= []) << id : heading.ids << id
75
+ heading.ids << id
197
76
  end
198
77
  end
199
78
  end
200
79
 
201
80
  def convert(parsed_result, render_parameter = {})
202
- @parsed_result = parsed_result
203
-
81
+ DEFAULT_TRANSFORMER.options[:render_parameter] = render_parameter
82
+ @parsed_result = DEFAULT_TRANSFORMER.transform parsed_result
204
83
  assign_id_to_headings
205
84
 
206
85
  children = parsed_result.children
207
86
  @context.file_basename = parsed_result.document_name
208
87
  @context.render_parameter = render_parameter
209
- if render_parameter[:nonpaged]
210
- @writers[Newpage] = @hr_writer
211
- end
212
88
  children.each {
213
89
  |node|
214
90
  to_html(node)