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 +14 -1
- data/README +8 -2
- data/Rakefile +19 -32
- data/TODO +1 -0
- data/friendly_format.gemspec +18 -16
- data/lib/friendly_format/adapter/abstract.rb +31 -0
- data/lib/friendly_format/adapter/hpricot_adapter.rb +34 -0
- data/lib/friendly_format/adapter/libxml_adapter.rb +25 -0
- data/lib/friendly_format/adapter/nokogiri_adapter.rb +21 -0
- data/lib/friendly_format/set_common.rb +5 -5
- data/lib/friendly_format/set_strict.rb +2 -2
- data/lib/friendly_format/version.rb +1 -1
- data/lib/friendly_format.rb +162 -99
- data/tasks/ann.rake +1 -1
- data/tasks/gem.rake +11 -2
- data/tasks/post_load.rake +2 -7
- data/tasks/rdoc.rake +4 -3
- data/tasks/setup.rb +35 -22
- data/test/sample/complex_article_result.txt +46 -46
- data/test/test_friendly_format.rb +92 -26
- metadata +22 -10
- data/tasks/manifest.rake +0 -48
data/CHANGES
CHANGED
@@ -1,4 +1,17 @@
|
|
1
|
-
=
|
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.
|
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
|
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
|
-
|
4
|
-
|
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.
|
40
|
-
|
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 !
|
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
data/friendly_format.gemspec
CHANGED
@@ -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
|
+
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{
|
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/
|
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 = ["--
|
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.
|
30
|
-
s.add_development_dependency(%q<
|
31
|
-
s.add_development_dependency(%q<
|
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<
|
34
|
-
s.add_dependency(%q<
|
35
|
-
s.add_dependency(%q<
|
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<
|
39
|
-
s.add_dependency(%q<
|
40
|
-
s.add_dependency(%q<
|
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([
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
data/lib/friendly_format.rb
CHANGED
@@ -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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
97
|
-
#
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
112
|
-
#
|
113
|
-
|
114
|
-
|
115
|
-
#
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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 > 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
|
-
|
164
|
+
e
|
165
|
+
|
136
166
|
end
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
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
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
151
|
-
|
152
|
-
|
201
|
+
if adapter.empty?(e)
|
202
|
+
"<#{e.name}>"
|
203
|
+
else
|
204
|
+
"<#{e.name}>" +
|
205
|
+
format_article_rec(e, allowed_tags) +
|
206
|
+
"</#{e.name}>"
|
207
|
+
end
|
153
208
|
end
|
209
|
+
|
154
210
|
end
|
155
|
-
|
156
|
-
|
157
|
-
end
|
211
|
+
}.join
|
212
|
+
end
|
158
213
|
|
159
|
-
|
160
|
-
|
161
|
-
|
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('<', '<').gsub('>', '>')
|
221
|
+
end
|
162
222
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|