friendly_format 0.5.1 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,4 +1,17 @@
1
- = pagify changes history
1
+ = friendly_format changes history
2
+
3
+ == friendly_format 0.6.1 / 2009-04-05
4
+
5
+ * added nokogiri and libxml-ruby support.
6
+ * drop support for hpricot < 0.7
7
+ * you can explicitly choose adapter, or auto-choose:
8
+ 1. hpricot or falls back to:
9
+ 2. nokogiri or falls back to:
10
+ 3. libxml-ruby
11
+ * now internally, we store string instead of symbol
12
+ for accepted tags to prevent from symbol explosion.
13
+ * ruby 1.9 compliant
14
+ * escape gt(>) in pre tag and forbidden tag as well.
2
15
 
3
16
  == friendly_format 0.5.1 / 2008-12-11
4
17
 
data/README CHANGED
@@ -1,4 +1,4 @@
1
- = friendly_format 0.5
1
+ = friendly_format 0.6
2
2
  by Lin Jen-Shin (a.k.a. godfat-真常[http://godfat.org])
3
3
  godfat (XD) godfat.org
4
4
 
@@ -18,9 +18,15 @@ by Lin Jen-Shin (a.k.a. godfat-真常[http://godfat.org])
18
18
  FriendlyFormat.format_article(text, FriendlyFormat::SetStrict.new)
19
19
  FriendlyFormat.format_article(text, FriendlyFormat::SetCommon.new)
20
20
 
21
+ FriendlyFormat.adapter = FriendlyFormat::NokogiriAdapter
22
+ FriendlyFormat.adapter = FriendlyFormat::LibxmlAdapter
23
+ FriendlyFormat.adapter = FriendlyFormat::HpricotAdapter # default
24
+
21
25
  == REQUIREMENTS:
22
26
 
23
- * hpricot 0.6 and later
27
+ * hpricot >=0.6 or
28
+ * nokogiri >=1.1 or
29
+ * libxml-ruby >=0.9
24
30
 
25
31
  == INSTALL:
26
32
 
data/Rakefile CHANGED
@@ -1,44 +1,26 @@
1
1
  # encoding: utf-8
2
2
 
3
- begin
4
- require 'bones'
5
- Bones.setup
6
- rescue LoadError
7
- load 'tasks/setup.rb' # this line should already be there
8
- end
3
+ require 'bones'
4
+ Bones.setup
9
5
 
10
6
  PROJ.name = 'friendly_format'
11
- # supress warnings, there's too many warnings in dm-core
12
- # PROJ.ruby_opts.delete '-w'
13
-
14
- PROJ.gem.dependencies << ['hpricot', '>=0.6.0']
15
- PROJ.gem.development_dependencies << ['minitest', '>=1.3.0']
16
- # PROJ.gem.executables = ["bin/#{PROJ.name}"]
17
-
18
- task :default do
19
- Rake.application.options.show_task_pattern = /./
20
- Rake.application.display_tasks_and_comments
21
- end
22
-
23
- namespace :gem do
24
- desc "create #{PROJ.name}.gemspec"
25
- task 'gemspec' do
26
- puts "rake gem:debug > #{PROJ.name}.gemspec"
27
- File.open("#{PROJ.name}.gemspec", 'w'){|spec| spec << `rake gem:debug`.sub(/.*/, '')}
28
- end
29
- end
30
-
31
7
  PROJ.authors = 'Lin Jen-Shin (a.k.a. godfat 真常)'
32
8
  PROJ.email = 'godfat (XD) godfat.org'
33
9
  PROJ.url = "http://github.com/godfat/#{PROJ.name}"
10
+ PROJ.rubyforge.name = 'ludy'
11
+
12
+ PROJ.gem.development_dependencies << ['hpricot', '>=0.6'] <<
13
+ ['nokogiri', '>=1.1'] <<
14
+ ['libxml-ruby', '>=0.9']
15
+
16
+ # PROJ.ruby_opts.delete '-w'
17
+
34
18
  PROJ.description = PROJ.summary = paragraphs_of('README', 'description').join("\n\n")
35
19
  PROJ.changes = paragraphs_of('CHANGES', 0..1).join("\n\n")
36
- PROJ.rubyforge.name = 'ludy'
37
20
  PROJ.version = File.read("lib/#{PROJ.name}/version.rb").gsub(/.*VERSION = '(.*)'.*/m, '\1')
38
21
 
39
- PROJ.manifest_file = 'Manifest'
40
- PROJ.exclude += ['^Manifest$', '^tmp', 'tmp$', '^pkg',
41
- '^\.gitignore$', '^ann-', '\.sqlite3$', '\.db$']
22
+ PROJ.exclude += ['^tmp', 'tmp$', '^pkg', '^\.gitignore$',
23
+ '^ann-', '\.sqlite3$', '\.db$']
42
24
 
43
25
  PROJ.rdoc.remote_dir = PROJ.name
44
26
 
@@ -46,8 +28,8 @@ PROJ.readme_file = 'README'
46
28
  PROJ.rdoc.main = 'README'
47
29
  PROJ.rdoc.exclude += ['Rakefile', '^tasks', '^test']
48
30
  PROJ.rdoc.include << '\w+'
49
- PROJ.rdoc.opts << '--diagram' if !WIN32 and `which dot` =~ %r/\/dot/
50
- PROJ.rdoc.opts += ['--charset=utf-8', '--inline-source',
31
+ # PROJ.rdoc.opts << '--diagram' if !Rake::Win32 and `which dot` =~ %r/\/dot/
32
+ PROJ.rdoc.opts += ['--charset=utf-8', '--inline-source',
51
33
  '--line-numbers', '--promiscuous']
52
34
 
53
35
  PROJ.spec.opts << '--color'
@@ -56,3 +38,8 @@ PROJ.ann.file = "ann-#{PROJ.name}-#{PROJ.version}"
56
38
  PROJ.ann.paragraphs.concat %w[LINKS SYNOPSIS REQUIREMENTS INSTALL LICENSE]
57
39
 
58
40
  CLEAN.include Dir['**/*.rbc']
41
+
42
+ task :default do
43
+ Rake.application.options.show_task_pattern = /./
44
+ Rake.application.display_tasks_and_comments
45
+ end
data/TODO CHANGED
@@ -1,4 +1,5 @@
1
1
  = friendly_article todo list
2
+
2
3
  * make format_article take an optional size argument that indicates
3
4
  how long should the result be. it should take &lt; as 1 character and
4
5
  <a href="/">嗯</a> as 1 character too. never cut out tag and unicode.
@@ -1,24 +1,23 @@
1
-
2
1
  # -*- encoding: utf-8 -*-
3
2
 
4
3
  Gem::Specification.new do |s|
5
4
  s.name = %q{friendly_format}
6
- s.version = "0.5.1"
5
+ s.version = "0.6.1"
7
6
 
8
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
8
  s.authors = ["Lin Jen-Shin (a.k.a. godfat \347\234\237\345\270\270)"]
10
- s.date = %q{2008-12-11}
11
- s.description = %q{}
9
+ s.date = %q{2009-04-05}
10
+ s.description = %q{make user input be valid xhtml and format it with gsub("\n", "<br/>") etc. you can partially allow some tags and don't escape them.}
12
11
  s.email = %q{godfat (XD) godfat.org}
13
12
  s.extra_rdoc_files = ["CHANGES", "LICENSE", "NOTICE", "README", "TODO", "friendly_format.gemspec"]
14
- s.files = ["CHANGES", "LICENSE", "NOTICE", "README", "Rakefile", "TODO", "friendly_format.gemspec", "lib/friendly_format.rb", "lib/friendly_format/set_common.rb", "lib/friendly_format/set_strict.rb", "lib/friendly_format/version.rb", "tasks/ann.rake", "tasks/bones.rake", "tasks/gem.rake", "tasks/git.rake", "tasks/manifest.rake", "tasks/notes.rake", "tasks/post_load.rake", "tasks/rdoc.rake", "tasks/rubyforge.rake", "tasks/setup.rb", "tasks/spec.rake", "tasks/svn.rake", "tasks/test.rake", "test/sample/complex_article.txt", "test/sample/complex_article_result.txt", "test/test_friendly_format.rb"]
13
+ s.files = ["CHANGES", "LICENSE", "NOTICE", "README", "Rakefile", "TODO", "friendly_format.gemspec", "lib/friendly_format.rb", "lib/friendly_format/adapter/abstract.rb", "lib/friendly_format/adapter/hpricot_adapter.rb", "lib/friendly_format/adapter/libxml_adapter.rb", "lib/friendly_format/adapter/nokogiri_adapter.rb", "lib/friendly_format/set_common.rb", "lib/friendly_format/set_strict.rb", "lib/friendly_format/version.rb", "test/sample/complex_article.txt", "test/sample/complex_article_result.txt", "test/test_friendly_format.rb"]
15
14
  s.has_rdoc = true
16
15
  s.homepage = %q{http://github.com/godfat/friendly_format}
17
- s.rdoc_options = ["--diagram", "--charset=utf-8", "--inline-source", "--line-numbers", "--promiscuous", "--main", "README"]
16
+ s.rdoc_options = ["--charset=utf-8", "--inline-source", "--line-numbers", "--promiscuous", "--main", "README"]
18
17
  s.require_paths = ["lib"]
19
18
  s.rubyforge_project = %q{ludy}
20
19
  s.rubygems_version = %q{1.3.1}
21
- s.summary = %q{}
20
+ s.summary = %q{make user input be valid xhtml and format it with gsub("\n", "<br/>") etc. you can partially allow some tags and don't escape them.}
22
21
  s.test_files = ["test/test_friendly_format.rb"]
23
22
 
24
23
  if s.respond_to? :specification_version then
@@ -26,17 +25,20 @@ Gem::Specification.new do |s|
26
25
  s.specification_version = 2
27
26
 
28
27
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
- s.add_runtime_dependency(%q<hpricot>, [">= 0.6.0"])
30
- s.add_development_dependency(%q<bones>, [">= 2.1.0"])
31
- s.add_development_dependency(%q<minitest>, [">= 1.3.0"])
28
+ s.add_development_dependency(%q<bones>, [">= 2.4.2"])
29
+ s.add_development_dependency(%q<hpricot>, [">= 0.6"])
30
+ s.add_development_dependency(%q<nokogiri>, [">= 1.1"])
31
+ s.add_development_dependency(%q<libxml-ruby>, [">= 0.9"])
32
32
  else
33
- s.add_dependency(%q<hpricot>, [">= 0.6.0"])
34
- s.add_dependency(%q<bones>, [">= 2.1.0"])
35
- s.add_dependency(%q<minitest>, [">= 1.3.0"])
33
+ s.add_dependency(%q<bones>, [">= 2.4.2"])
34
+ s.add_dependency(%q<hpricot>, [">= 0.6"])
35
+ s.add_dependency(%q<nokogiri>, [">= 1.1"])
36
+ s.add_dependency(%q<libxml-ruby>, [">= 0.9"])
36
37
  end
37
38
  else
38
- s.add_dependency(%q<hpricot>, [">= 0.6.0"])
39
- s.add_dependency(%q<bones>, [">= 2.1.0"])
40
- s.add_dependency(%q<minitest>, [">= 1.3.0"])
39
+ s.add_dependency(%q<bones>, [">= 2.4.2"])
40
+ s.add_dependency(%q<hpricot>, [">= 0.6"])
41
+ s.add_dependency(%q<nokogiri>, [">= 1.1"])
42
+ s.add_dependency(%q<libxml-ruby>, [">= 0.9"])
41
43
  end
42
44
  end
@@ -0,0 +1,31 @@
1
+
2
+ module FriendlyFormat
3
+ module Abstract
4
+
5
+ def method_name node
6
+ # discard body
7
+ node.children.to_s
8
+ end
9
+
10
+ def to_xhtml node
11
+ node.to_xhtml
12
+ end
13
+
14
+ def content node
15
+ node.to_s
16
+ end
17
+
18
+ def element? node
19
+ node.element?
20
+ end
21
+
22
+ def text? node
23
+ node.text?
24
+ end
25
+
26
+ def empty? node
27
+ node.children.empty?
28
+ end
29
+
30
+ end # of Abstract
31
+ end # of FriendlyFormat
@@ -0,0 +1,34 @@
1
+
2
+ require 'hpricot'
3
+
4
+ module FriendlyFormat
5
+ class HpricotAdapter
6
+ class << self
7
+
8
+ def parse html
9
+ Hpricot.parse(html)
10
+ end
11
+
12
+ def to_xhtml node
13
+ node.to_html
14
+ end
15
+
16
+ def content node
17
+ node.content
18
+ end
19
+
20
+ def element? node
21
+ node.kind_of?(Hpricot::Elem)
22
+ end
23
+
24
+ def text? node
25
+ node.kind_of?(Hpricot::Text)
26
+ end
27
+
28
+ def empty? node
29
+ node.empty?
30
+ end
31
+
32
+ end # of class method for HpricotAdapter
33
+ end # of HpricotAdapter
34
+ end # of FriendlyFormat
@@ -0,0 +1,25 @@
1
+
2
+ require 'friendly_format/adapter/abstract'
3
+ require 'libxml'
4
+
5
+ module FriendlyFormat
6
+ class LibxmlAdapter
7
+ extend Abstract
8
+
9
+ def self.parse html
10
+ parser = LibXML::XML::HTMLParser.string(
11
+ "<zzz>#{html}</zzz>",
12
+ :options => LibXML::XML::HTMLParser::Options::RECOVER)
13
+
14
+ # root is html, children is [body], first is body
15
+ # same as nokogiri
16
+ # drop zzz with .children.first since it would wrap a tag p for the article
17
+ parser.parse.root.children.first.children.first
18
+ end
19
+
20
+ def self.to_xhtml node
21
+ node.to_s
22
+ end
23
+
24
+ end # of LibxmlAdapter
25
+ end # of FriendlyFormat
@@ -0,0 +1,21 @@
1
+
2
+ require 'friendly_format/adapter/abstract'
3
+ require 'nokogiri'
4
+
5
+ module FriendlyFormat
6
+ class NokogiriAdapter
7
+ extend Abstract
8
+
9
+ def self.parse html
10
+ # root is html, children is [body], first is body
11
+ # same as libxml
12
+ # drop zzz with .children.first since it would wrap a tag p for the article
13
+ Nokogiri::HTML.parse(
14
+ "<zzz>#{html}</zzz>",
15
+ nil, # url?
16
+ html.respond_to?(:encoding) ? html.encoding.name : 'utf-8'
17
+ ).root.children.first.children.first
18
+ end
19
+
20
+ end # of NokogiriAdapter
21
+ end # of FriendlyFormat
@@ -6,11 +6,11 @@ module FriendlyFormat
6
6
  # a few people have permission to post or edit.
7
7
  class SetCommon < Set
8
8
  def initialize
9
- super([ :a, :area, :b, :big, :blockquote, :br,
10
- :center, :cite, :code, :del, :div, :em,
11
- :font, :h1, :h2, :h3, :h4, :h5, :h6, :hr,
12
- :i, :img, :li, :map, :object, :ol, :p,
13
- :pre, :small, :span, :strong, :u, :ul ])
9
+ super(%w[ a area b big blockquote br
10
+ center cite code del div em
11
+ font h1 h2 h3 h4 h5 h6 hr
12
+ i img li map object ol p
13
+ pre small span strong u ul ])
14
14
  end
15
15
  end
16
16
  end
@@ -5,8 +5,8 @@ module FriendlyFormat
5
5
  # to post or edit articles.
6
6
  class SetStrict < Set
7
7
  def initialize
8
- super([ :a, :b, :code, :del, :em, :font,
9
- :i, :img, :li, :ol, :strong, :u, :ul ])
8
+ super(%w[ a b code del em font
9
+ i img li ol strong u ul ])
10
10
  end
11
11
  end
12
12
  end
@@ -1,3 +1,3 @@
1
1
  module FriendlyFormat
2
- VERSION = '0.5.1'
2
+ VERSION = '0.6.1'
3
3
  end
@@ -5,21 +5,45 @@ require 'friendly_format/set_strict'
5
5
 
6
6
  # 2008-05-09 godfat
7
7
  module FriendlyFormat
8
+ autoload(:LibxmlAdapter, 'friendly_format/adapter/libxml_adapter')
9
+ autoload(:HpricotAdapter, 'friendly_format/adapter/hpricot_adapter')
10
+ autoload(:NokogiriAdapter, 'friendly_format/adapter/nokogiri_adapter')
11
+
12
+ class << self
13
+ attr_writer(:adapter)
14
+ def adapter
15
+ @adapter ||= begin
16
+ HpricotAdapter
17
+ rescue LoadError
18
+ begin
19
+ NokogiriAdapter
20
+ rescue LoadError
21
+ LibxmlAdapter
22
+ end
23
+ end
24
+ end
25
+ end
26
+
8
27
  module_function
9
28
  # format entire article for you, passing allowed tags to it.
10
29
  # you can use Set or Symbol to specify which tags would be allowed.
11
30
  # default was no tags at all, all tags would be escaped.
12
31
  # it uses Hpricot to parse input.
13
32
  def format_article html, *args
14
- FriendlyFormat.format_article_entrance(html,
15
- args.inject(Set.new){ |allowed_tags, arg|
16
- case arg
17
- when Symbol; allowed_tags << arg
18
- when Set; allowed_tags += arg
19
- else; raise(TypeError.new("expected Symbol or Set, got #{arg.class}"))
20
- end
21
- allowed_tags
22
- })
33
+ return html if html.strip == ''
34
+
35
+ FriendlyFormat.force_encoding(
36
+ FriendlyFormat.format_article_entrance(html,
37
+ args.inject(Set.new){ |allowed_tags, arg|
38
+ case arg
39
+ when String; allowed_tags << arg
40
+ when Symbol; allowed_tags << arg.to_s
41
+ when Set; allowed_tags += Set.new(arg.map{|a|a.to_s})
42
+ else; raise(TypeError.new("expected String|Symbol|Set, got #{arg.class}"))
43
+ end
44
+ allowed_tags
45
+ }),
46
+ html)
23
47
  end
24
48
 
25
49
  # automaticly add "a href" tag on text starts from
@@ -27,14 +51,12 @@ module FriendlyFormat
27
51
  # regexp translated from drupal to find where's the target.
28
52
  # it uses simplified regexp to do the task. see format_url.
29
53
  def format_autolink html, attrs = {}
30
- require 'hpricot'
31
-
32
- doc = Hpricot.parse html
33
- doc.each_child{ |c|
34
- next unless c.kind_of?(Hpricot::Text)
35
- c.content = format_url c.content, attrs
36
- }
37
- doc.to_html
54
+ return html if html.strip == ''
55
+
56
+ FriendlyFormat.force_encoding(
57
+ FriendlyFormat.format_autolink_rec(
58
+ FriendlyFormat.adapter.parse(html), attrs),
59
+ html)
38
60
  end
39
61
 
40
62
  # translated from drupal-6.2/modules/filter/filter.module
@@ -65,27 +87,6 @@ module FriendlyFormat
65
87
  }[1..-1]
66
88
  end
67
89
 
68
- # same as format_autolink_regexp, but it's simplified and
69
- # cannot process text composed with html and plain text.
70
- # used in format_autolink.
71
- def format_url text, attrs = {}
72
- # translated from drupal-6.2/modules/filter/filter.module
73
- # Match absolute URLs.
74
- text.gsub(
75
- %r{((http://|https://|ftp://|mailto:|smb://|afp://|file://|gopher://|news://|ssl://|sslv2://|sslv3://|tls://|tcp://|udp://|www\.)([a-zA-Z0-9@:%_+*~#?&=.,/;-]*[a-zA-Z0-9@:%_+*~#&=/;-]))([.,?!]*?)}i){ |match|
76
- url = $1 # is there any other way to get this variable?
77
-
78
- caption = FriendlyFormat.trim url
79
- attrs = attrs.map{ |k,v| " #{k}=\"#{v}\""}.join
80
-
81
- # Match www domains/addresses.
82
- url = "http://#{url}" unless url =~ %r{^http://}
83
- "<a href=\"#{url}\" title=\"#{url}\"#{attrs}>#{caption}</a>"
84
- # Match e-mail addresses.
85
- }.gsub( %r{([A-Za-z0-9._-]+@[A-Za-z0-9._+-]+\.[A-Za-z]{2,4})([.,?!]*?)}i,
86
- '<a href="mailto:\1">\1</a>')
87
- end
88
-
89
90
  # convert newline character(s) to <br />
90
91
  def format_newline text
91
92
  # windows: \r\n
@@ -93,79 +94,141 @@ module FriendlyFormat
93
94
  text.gsub("\r\n", "\n").tr("\r", "\n").gsub("\n", '<br />')
94
95
  end
95
96
 
96
- private
97
- # extract it to public?
98
- def self.trim text, length = 75
99
- # Use +3 for '...' string length.
100
- if text.size <= 3
101
- '...'
102
- elsif text.size > length
103
- "#{text[0...length-3]}..."
104
- else
105
- text
97
+
98
+ # private below
99
+
100
+ class << self
101
+ # extract it to public?
102
+ # @api private
103
+ def trim text, length = 75
104
+ # Use +3 for '...' string length.
105
+ if text.size <= 3
106
+ '...'
107
+ elsif text.size > length
108
+ "#{text[0...length-3]}..."
109
+ else
110
+ text
111
+ end
106
112
  end
107
- end
108
113
 
109
- # perhaps we should escape all inside code instead of pre?
110
- def self.escape_all_inside_pre html, allowed_tags
111
- return html unless allowed_tags.member? :pre
112
- # don't bother nested pre, because we escape all tags in pre
113
- html = html + '</pre>' unless html =~ %r{</pre>}i
114
- html.gsub(%r{<pre>(.*)</pre>}mi){
115
- # stop escaping for '>' because drupal's url filter would make &gt; into url...
116
- # is there any other way to get matched group?
117
- "<pre>#{FriendlyFormat.escape_lt(FriendlyFormat.escape_amp($1))}</pre>"
118
- }
119
- end
114
+ # same as format_autolink_regexp, but it's simplified and
115
+ # cannot process text composed with html and plain text.
116
+ # used in format_autolink.
117
+ # @api private
118
+ def format_url text, attrs = {}
119
+ # translated from drupal-6.2/modules/filter/filter.module
120
+ # Match absolute URLs.
121
+ text.gsub(
122
+ %r{((http://|https://|ftp://|mailto:|smb://|afp://|file://|gopher://|news://|ssl://|sslv2://|sslv3://|tls://|tcp://|udp://|www\.)([a-zA-Z0-9@:%_+*~#?&=.,/;-]*[a-zA-Z0-9@:%_+*~#&=/;-]))([.,?!]*?)}i){ |match|
123
+ url = $1 # is there any other way to get this variable?
124
+ caption = trim(url)
125
+ html_attrs = attrs.map{ |k,v| " #{k}=\"#{v}\""}.join
126
+
127
+ # Match www domains/addresses.
128
+ url = "http://#{url}" unless url =~ %r{^http://}
129
+ "<a href=\"#{url}\" title=\"#{url}\"#{html_attrs}>#{caption}</a>"
130
+ # Match e-mail addresses.
131
+ }.gsub( %r{([A-Za-z0-9._-]+@[A-Za-z0-9._+-]+\.[A-Za-z]{2,4})([.,?!]*?)}i,
132
+ '<a href="mailto:\1">\1</a>')
133
+ end
120
134
 
121
- # recursion entrance
122
- def self.format_article_entrance html, allowed_tags = Set.new
123
- require 'hpricot'
124
- FriendlyFormat.format_article_elems(Hpricot.parse(
125
- FriendlyFormat.escape_all_inside_pre(html, allowed_tags)), allowed_tags)
126
- end
135
+ # perhaps we should escape all inside code instead of pre?
136
+ # @api private
137
+ def escape_ltgt_inside_pre html, allowed_tags
138
+ return html unless allowed_tags.member?('pre')
139
+ # don't bother nested pre, because we escape all tags in pre
140
+ html = html + '</pre>' unless html =~ %r{</pre>}i
141
+ html.gsub(%r{<pre>(.*)</pre>}mi){
142
+ # stop escaping for '>' because drupal's url filter would make &gt; into url...
143
+ # is there any other way to get matched group?
144
+ "<pre>#{escape_ltgt($1)}</pre>"
145
+ }
146
+ end
147
+
148
+ # @api private
149
+ def format_autolink_rec elem, attrs = {}
150
+ elem.children.map{ |e|
151
+ if adapter.text?(e)
152
+ format_url(e.content, attrs)
153
+
154
+ elsif adapter.element?(e)
155
+ if adapter.empty?(e)
156
+ adapter.to_xhtml(e)
157
+ else
158
+ "<#{e.name}>" +
159
+ format_autolink_rec(e, attrs) +
160
+ "</#{e.name}>"
161
+ end
127
162
 
128
- # recursion
129
- def self.format_article_elems elems, allowed_tags = Set.new, no_format_newline = false
130
- elems.children.map{ |e|
131
- if e.kind_of?(Hpricot::Text)
132
- if no_format_newline
133
- format_url(e.content)
134
163
  else
135
- format_newline format_url(e.content)
164
+ e
165
+
136
166
  end
137
- elsif e.kind_of?(Hpricot::Elem)
138
- if allowed_tags.member? e.name.to_sym
139
- if e.empty? || e.name == 'a'
140
- e.to_html
167
+
168
+ }.join
169
+ end
170
+
171
+ # recursion entrance
172
+ # @api private
173
+ def format_article_entrance html, allowed_tags = Set.new
174
+ format_article_rec(
175
+ adapter.parse(escape_ltgt_inside_pre(html, allowed_tags)),
176
+ allowed_tags)
177
+ end
178
+
179
+ # recursion
180
+ # @api private
181
+ def format_article_rec elem, allowed_tags = Set.new, no_format_newline = false
182
+ elem.children.map{ |e|
183
+ if adapter.text?(e)
184
+ if no_format_newline
185
+ format_url(adapter.content(e))
141
186
  else
142
- e.stag.inspect +
143
- FriendlyFormat.format_article_elems(e, allowed_tags, e.stag.name == 'pre') +
144
- (e.etag || Hpricot::ETag.new(e.stag.name)).inspect
187
+ format_newline(format_url(adapter.content(e)))
145
188
  end
146
- else
147
- if e.empty?
148
- FriendlyFormat.escape_lt(e.stag.inspect)
189
+
190
+ elsif adapter.element?(e)
191
+ if allowed_tags.member?(e.name)
192
+ if adapter.empty?(e) || e.name == 'a'
193
+ adapter.to_xhtml(e)
194
+ else
195
+ "<#{e.name}>" +
196
+ format_article_rec(
197
+ e, allowed_tags, e.name == 'pre') +
198
+ "</#{e.name}>"
199
+ end
149
200
  else
150
- FriendlyFormat.escape_lt(e.stag.inspect) +
151
- FriendlyFormat.format_article_elems(e, allowed_tags) +
152
- FriendlyFormat.escape_lt((e.etag || Hpricot::ETag.new(e.stag.name)).inspect)
201
+ if adapter.empty?(e)
202
+ "&lt;#{e.name}&gt;"
203
+ else
204
+ "&lt;#{e.name}&gt;" +
205
+ format_article_rec(e, allowed_tags) +
206
+ "&lt;/#{e.name}&gt;"
207
+ end
153
208
  end
209
+
154
210
  end
155
- end
156
- }.join
157
- end
211
+ }.join
212
+ end
158
213
 
159
- def self.escape_amp text
160
- text.gsub('&', '&amp;')
161
- end
214
+ # i cannot find a way to escape both lt and gt,
215
+ # so it's a trick that just escape lt and no browser
216
+ # would treat complex lt and gt structure to be a tag
217
+ # wraping content.
218
+ # @api private
219
+ def escape_ltgt text
220
+ text.gsub('<', '&lt;').gsub('>', '&gt;')
221
+ end
162
222
 
163
- # i cannot find a way to escape both lt and gt,
164
- # so it's a trick that just escape lt and no browser
165
- # would treat complex lt and gt structure to be a tag
166
- # wraping content.
167
- def self.escape_lt text
168
- text.gsub('<', '&lt;')
169
- end
223
+ # force encoding for ruby 1.9
224
+ # @api private
225
+ def force_encoding output, input
226
+ if output.respond_to?(:force_encoding)
227
+ output.force_encoding(input.encoding)
228
+ else
229
+ output
230
+ end
231
+ end
170
232
 
233
+ end
171
234
  end
data/tasks/ann.rake CHANGED
@@ -42,7 +42,7 @@ namespace :ann do
42
42
  desc "Send an email announcement"
43
43
  task :email => ['ann:prereqs', PROJ.ann.file] do
44
44
  ann = PROJ.ann
45
- from = ann.email[:from] || PROJ.email
45
+ from = ann.email[:from] || Array(PROJ.authors).first || PROJ.email
46
46
  to = Array(ann.email[:to])
47
47
 
48
48
  ### build a mail header for RFC 822