nora_mark 0.2beta7 → 0.2beta8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)