friendly_format 0.5.1 → 0.6.1

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.
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