wikicloth 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README +81 -0
- data/Rakefile +23 -0
- data/init.rb +1 -0
- data/lib/core_ext.rb +43 -0
- data/lib/wiki_buffer/html_element.rb +237 -0
- data/lib/wiki_buffer/link.rb +70 -0
- data/lib/wiki_buffer/table.rb +159 -0
- data/lib/wiki_buffer/var.rb +77 -0
- data/lib/wiki_buffer.rb +279 -0
- data/lib/wiki_cloth.rb +61 -0
- data/lib/wiki_link_handler.rb +138 -0
- data/lib/wikicloth.rb +5 -0
- data/run_tests.rb +48 -0
- data/sample_documents/air_force_one.wiki +170 -0
- data/sample_documents/cheatsheet.wiki +205 -0
- data/sample_documents/default.css +34 -0
- data/sample_documents/elements.wiki +7 -0
- data/sample_documents/george_washington.wiki +526 -0
- data/sample_documents/images.wiki +15 -0
- data/sample_documents/lists.wiki +421 -0
- data/sample_documents/pipe_trick.wiki +68 -0
- data/sample_documents/random.wiki +55 -0
- data/sample_documents/tv.wiki +312 -0
- data/sample_documents/wiki.png +0 -0
- data/sample_documents/wiki_tables.wiki +410 -0
- data/tasks/wikicloth_tasks.rake +0 -0
- data/test/test_helper.rb +3 -0
- data/test/wiki_cloth_test.rb +8 -0
- data/wikicloth.gemspec +69 -0
- metadata +100 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
Ruby implementation of the MediaWiki markup language.
|
2
|
+
|
3
|
+
Supports
|
4
|
+
---------------------------------------------------
|
5
|
+
|
6
|
+
* Variables, Templates {{ ... }}
|
7
|
+
* Links
|
8
|
+
o External Links [ ... ]
|
9
|
+
o Internal Links, Images [[ ... ]]
|
10
|
+
+ (see also pipe trick)
|
11
|
+
* Wikimedia Markup
|
12
|
+
o == Headings ==
|
13
|
+
o Lists (*#;:)
|
14
|
+
o bold ('''), italic ('') or both (''''')
|
15
|
+
o Horizontal rule (----)
|
16
|
+
o Tables
|
17
|
+
* <code>,<nowiki>,<pre> (disable wiki markup)
|
18
|
+
o space at the beginning of a line (<pre>)
|
19
|
+
* <ref> and <references/> support
|
20
|
+
* html sanitization
|
21
|
+
|
22
|
+
Install
|
23
|
+
---------------------------------------------------
|
24
|
+
|
25
|
+
git clone git://github.com/nricciar/wikicloth.git
|
26
|
+
cd wikicloth/
|
27
|
+
gem build wikicloth.gemspec
|
28
|
+
sudo gem install wikicloth-0.1.3.gem
|
29
|
+
|
30
|
+
Usage
|
31
|
+
---------------------------------------------------
|
32
|
+
|
33
|
+
include WikiCloth
|
34
|
+
|
35
|
+
@wiki = WikiCloth.new({
|
36
|
+
:data => "<nowiki>{{test}}</nowiki> ''Hello {{test}}!''\n",
|
37
|
+
:params => { "test" => "World" } })
|
38
|
+
@wiki.to_html => "<p>{{test}} <i>Hello World!</i></p>"
|
39
|
+
|
40
|
+
|
41
|
+
Wiki Links and Variable/Template Handling
|
42
|
+
---------------------------------------------------
|
43
|
+
|
44
|
+
Use the url_for and link_attributes_for methods to override the default URL for
|
45
|
+
an [[internal link]]. If you need even more control, the link_for can also be
|
46
|
+
used to return raw html.
|
47
|
+
|
48
|
+
class CustomLinkHandler < WikiCloth::WikiLinkHandler
|
49
|
+
|
50
|
+
def url_for(page)
|
51
|
+
"javascript:alert('You clicked on: #{page}');"
|
52
|
+
end
|
53
|
+
|
54
|
+
def link_attributes_for(page)
|
55
|
+
{ :href => url_for(page) }
|
56
|
+
end
|
57
|
+
|
58
|
+
def include_resource(resource,options=[])
|
59
|
+
case resource
|
60
|
+
when "date"
|
61
|
+
Time.now.to_s
|
62
|
+
else
|
63
|
+
# default behavior
|
64
|
+
super(resource,options)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
@wiki = WikiCloth::WikiCloth.new({
|
72
|
+
:params => { "PAGENAME" => "Testing123" },
|
73
|
+
:link_handler => CustomLinkHandler.new,
|
74
|
+
:data => "Hello World From {{ PAGENAME }} on {{ date }}\n"
|
75
|
+
})
|
76
|
+
|
77
|
+
@wiki.to_html =>
|
78
|
+
<p>
|
79
|
+
<a href="javascript:alert('You clicked on: Hello World');">Hello World</a> From Testing123 on Wed Jul 08 22:23:44 -0400 2009
|
80
|
+
</p>
|
81
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the wikicloth plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.libs << 'test'
|
12
|
+
t.pattern = 'test/**/*_test.rb'
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Generate documentation for the wikicloth plugin.'
|
17
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
18
|
+
rdoc.rdoc_dir = 'rdoc'
|
19
|
+
rdoc.title = 'WikiCloth'
|
20
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
21
|
+
rdoc.rdoc_files.include('README')
|
22
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
23
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "lib", "wikicloth")
|
data/lib/core_ext.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module ExtendedString
|
2
|
+
|
3
|
+
def blank?
|
4
|
+
respond_to?(:empty?) ? empty? : !self
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_slug
|
8
|
+
self.gsub(/\W+/, '-').gsub(/^-+/,'').gsub(/-+$/,'').downcase
|
9
|
+
end
|
10
|
+
|
11
|
+
def auto_link
|
12
|
+
url_check = Regexp.new( '(^|[\n ])([\w]+?://[\w]+[^ \"\r\n\t<]*)', Regexp::MULTILINE | Regexp::IGNORECASE )
|
13
|
+
www_check = Regexp.new( '(^|[\n ])((www)\.[^ \"\t\n\r<]*)', Regexp::MULTILINE | Regexp::IGNORECASE )
|
14
|
+
self.gsub!(url_check, '\1<a href="\2">\2</a>')
|
15
|
+
self.gsub!(www_check, '\1<a href="http://\2">\2</a>')
|
16
|
+
to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def dump()
|
20
|
+
ret = to_s
|
21
|
+
delete!(to_s)
|
22
|
+
ret
|
23
|
+
end
|
24
|
+
|
25
|
+
def smart_split(char)
|
26
|
+
ret = []
|
27
|
+
tmp = ""
|
28
|
+
inside = 0
|
29
|
+
to_s.each_char do |x|
|
30
|
+
if x == char && inside == 0
|
31
|
+
ret << tmp
|
32
|
+
tmp = ""
|
33
|
+
else
|
34
|
+
inside += 1 if x == "[" || x == "{" || x == "<"
|
35
|
+
inside -= 1 if x == "]" || x == "}" || x == ">"
|
36
|
+
tmp += x
|
37
|
+
end
|
38
|
+
end
|
39
|
+
ret << tmp unless tmp.empty?
|
40
|
+
ret
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'builder'
|
3
|
+
|
4
|
+
module WikiCloth
|
5
|
+
|
6
|
+
class WikiBuffer::HTMLElement < WikiBuffer
|
7
|
+
|
8
|
+
ALLOWED_ELEMENTS = ['a','b','i','div','span','sup','sub','strike','s','u','font','big','ref','tt','del',
|
9
|
+
'small','blockquote','strong','pre','code','references','ol','li','ul','dd','dt','dl','center',
|
10
|
+
'h2','h3','h4','h5','h6']
|
11
|
+
ALLOWED_ATTRIBUTES = ['id','name','style','class','href','start','value']
|
12
|
+
ESCAPED_TAGS = [ 'nowiki', 'pre', 'code' ]
|
13
|
+
SHORT_TAGS = [ 'meta','br','hr','img' ]
|
14
|
+
NO_NEED_TO_CLOSE = ['li','p'] + SHORT_TAGS
|
15
|
+
|
16
|
+
def initialize(d="",options={},check=nil)
|
17
|
+
super("",options)
|
18
|
+
self.buffer_type = "Element"
|
19
|
+
@in_quotes = false
|
20
|
+
@in_single_quotes = false
|
21
|
+
@start_tag = 1
|
22
|
+
@tag_check = check unless check.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def run_globals?
|
26
|
+
return ESCAPED_TAGS.include?(self.element_name) ? false : true
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
if NO_NEED_TO_CLOSE.include?(self.element_name)
|
31
|
+
return "<#{self.element_name} />" if SHORT_TAGS.include?(self.element_name)
|
32
|
+
return "</#{self.element_name}><#{self.element_name}>" if @tag_check == self.element_name
|
33
|
+
end
|
34
|
+
|
35
|
+
if ESCAPED_TAGS.include?(self.element_name)
|
36
|
+
# escape all html inside this element
|
37
|
+
self.element_content = self.element_content.gsub('<','<').gsub('>','>')
|
38
|
+
# hack to fix <code><nowiki> nested mess
|
39
|
+
self.element_content = self.element_content.gsub(/<[\/]*\s*nowiki\s*>/,'')
|
40
|
+
end
|
41
|
+
|
42
|
+
lhandler = @options[:link_handler]
|
43
|
+
case self.element_name
|
44
|
+
when "ref"
|
45
|
+
self.element_name = "sup"
|
46
|
+
named_ref = self.name_attribute
|
47
|
+
ref = lhandler.find_reference_by_name(named_ref) unless named_ref.nil?
|
48
|
+
if ref.nil?
|
49
|
+
lhandler.references << { :name => named_ref, :value => self.element_content, :count => 0 }
|
50
|
+
ref = lhandler.references.last
|
51
|
+
end
|
52
|
+
ref_id = (named_ref.nil? ? "" : "#{named_ref}_") + "#{lhandler.reference_index(ref)}-#{ref[:count]}"
|
53
|
+
self.params << { :name => "id", :value => "cite_ref-#{ref_id}" }
|
54
|
+
self.params << { :name => "class", :value => "reference" }
|
55
|
+
self.element_content = "[<a href=\"#cite_note-" + (named_ref.nil? ? "" : "#{named_ref}_") +
|
56
|
+
"#{lhandler.reference_index(ref)}\">#{lhandler.reference_index(ref)}</a>]"
|
57
|
+
ref[:count] += 1
|
58
|
+
when "references"
|
59
|
+
ref_count = 0
|
60
|
+
self.element_name = "ol"
|
61
|
+
self.element_content = lhandler.references.collect { |r|
|
62
|
+
ref_count += 1
|
63
|
+
ref_name = (r[:name].nil? ? "" : r[:name].to_slug + "_")
|
64
|
+
ret = "<li id=\"cite_note-#{ref_name}#{ref_count}\"><b>"
|
65
|
+
1.upto(r[:count]) { |x| ret += "<a href=\"#cite_ref-#{ref_name}#{ref_count}-#{x-1}\">" +
|
66
|
+
(r[:count] == 1 ? "^" : (x-1).to_s(26).tr('0-9a-p', 'a-z')) + "</a> " }
|
67
|
+
ret += "</b> #{r[:value]}</li>"
|
68
|
+
}.to_s
|
69
|
+
when "nowiki"
|
70
|
+
return self.element_content
|
71
|
+
end
|
72
|
+
|
73
|
+
tmp = elem.tag!(self.element_name, self.element_attributes) { |x| x << self.element_content }
|
74
|
+
unless ALLOWED_ELEMENTS.include?(self.element_name)
|
75
|
+
tmp.gsub!(/[\-!\|&"\{\}\[\]]/) { |r| self.escape_char(r) }
|
76
|
+
return tmp.gsub('<', '<').gsub('>', '>')
|
77
|
+
end
|
78
|
+
tmp
|
79
|
+
end
|
80
|
+
|
81
|
+
def name_attribute
|
82
|
+
params.each { |p| return p[:value].to_slug if p.kind_of?(Hash) && p[:name] == "name" }
|
83
|
+
return nil
|
84
|
+
end
|
85
|
+
|
86
|
+
def element_attributes
|
87
|
+
attr = {}
|
88
|
+
params.each { |p| attr[p[:name]] = p[:value] if p.kind_of?(Hash) }
|
89
|
+
if ALLOWED_ELEMENTS.include?(self.element_name.strip.downcase)
|
90
|
+
attr.delete_if { |key,value| !ALLOWED_ATTRIBUTES.include?(key.strip) }
|
91
|
+
end
|
92
|
+
return attr
|
93
|
+
end
|
94
|
+
|
95
|
+
def element_name
|
96
|
+
@ename ||= ""
|
97
|
+
end
|
98
|
+
|
99
|
+
def element_content
|
100
|
+
@econtent ||= ""
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
|
105
|
+
def escape_char(c)
|
106
|
+
c = case c
|
107
|
+
when '-' then '-'
|
108
|
+
when '!' then '!'
|
109
|
+
when '|' then '|'
|
110
|
+
when '&' then '&'
|
111
|
+
when '"' then '"'
|
112
|
+
when '{' then '{'
|
113
|
+
when '}' then '}'
|
114
|
+
when '[' then '['
|
115
|
+
when ']' then ']'
|
116
|
+
when '*' then '*'
|
117
|
+
when '#' then '#'
|
118
|
+
when ':' then ':'
|
119
|
+
when ';' then ';'
|
120
|
+
when "'" then '''
|
121
|
+
when '=' then '='
|
122
|
+
else
|
123
|
+
c
|
124
|
+
end
|
125
|
+
return c
|
126
|
+
end
|
127
|
+
|
128
|
+
def elem
|
129
|
+
Builder::XmlMarkup.new
|
130
|
+
end
|
131
|
+
|
132
|
+
def element_name=(val)
|
133
|
+
@ename = val
|
134
|
+
end
|
135
|
+
|
136
|
+
def element_content=(val)
|
137
|
+
@econtent = val
|
138
|
+
end
|
139
|
+
|
140
|
+
def in_quotes?
|
141
|
+
@in_quotes || @in_single_quotes ? true : false
|
142
|
+
end
|
143
|
+
|
144
|
+
def new_char()
|
145
|
+
case
|
146
|
+
# tag name
|
147
|
+
when @start_tag == 1 && current_char == ' '
|
148
|
+
self.element_name = self.data.strip.downcase
|
149
|
+
self.data = ""
|
150
|
+
@start_tag = 2
|
151
|
+
|
152
|
+
# tag is closed <tag/> no attributes
|
153
|
+
when @start_tag == 1 && previous_char == '/' && current_char == '>'
|
154
|
+
self.data.chop!
|
155
|
+
self.element_name = self.data.strip.downcase
|
156
|
+
self.data = ""
|
157
|
+
@start_tag = 0
|
158
|
+
return false
|
159
|
+
|
160
|
+
# open tag
|
161
|
+
when @start_tag == 1 && previous_char != '/' && current_char == '>'
|
162
|
+
self.element_name = self.data.strip.downcase
|
163
|
+
self.data = ""
|
164
|
+
@start_tag = 0
|
165
|
+
return false if SHORT_TAGS.include?(self.element_name)
|
166
|
+
return false if self.element_name == @tag_check && NO_NEED_TO_CLOSE.include?(self.element_name)
|
167
|
+
|
168
|
+
# new tag attr
|
169
|
+
when @start_tag == 2 && current_char == ' ' && self.in_quotes? == false
|
170
|
+
self.current_param = self.data
|
171
|
+
self.data = ""
|
172
|
+
self.params << ""
|
173
|
+
|
174
|
+
# tag attribute name
|
175
|
+
when @start_tag == 2 && current_char == '=' && self.in_quotes? == false
|
176
|
+
self.current_param = self.data
|
177
|
+
self.data = ""
|
178
|
+
self.name_current_param()
|
179
|
+
|
180
|
+
# tag is now open
|
181
|
+
when @start_tag == 2 && previous_char != '/' && current_char == '>'
|
182
|
+
self.current_param = self.data
|
183
|
+
self.data = ""
|
184
|
+
@start_tag = 0
|
185
|
+
return false if SHORT_TAGS.include?(self.element_name)
|
186
|
+
return false if self.element_name == @tag_check && NO_NEED_TO_CLOSE.include?(self.element_name)
|
187
|
+
|
188
|
+
# tag is closed <example/>
|
189
|
+
when @start_tag == 2 && previous_char == '/' && current_char == '>'
|
190
|
+
self.current_param = self.data.chop
|
191
|
+
self.data = ""
|
192
|
+
@start_tag = 0
|
193
|
+
return false
|
194
|
+
|
195
|
+
# in quotes
|
196
|
+
when @start_tag == 2 && current_char == "'" && previous_char != '\\' && !@in_quotes
|
197
|
+
@in_single_quotes = !@in_single_quotes
|
198
|
+
|
199
|
+
# in quotes
|
200
|
+
when @start_tag == 2 && current_char == '"' && previous_char != '\\' && !@in_single_quotes
|
201
|
+
@in_quotes = !@in_quotes
|
202
|
+
|
203
|
+
# start of a closing tag
|
204
|
+
when @start_tag == 0 && previous_char == '<' && current_char == '/'
|
205
|
+
self.element_content += self.data.chop
|
206
|
+
self.data = ""
|
207
|
+
@start_tag = 5
|
208
|
+
|
209
|
+
when @start_tag == 5 && (current_char == '>' || current_char == ' ') && !self.data.blank?
|
210
|
+
self.data = self.data.strip.downcase
|
211
|
+
if self.data == self.element_name
|
212
|
+
self.data = ""
|
213
|
+
return false
|
214
|
+
else
|
215
|
+
if @tag_check == self.data && NO_NEED_TO_CLOSE.include?(self.element_name)
|
216
|
+
self.data = "</#{self.data}>"
|
217
|
+
return false
|
218
|
+
else
|
219
|
+
self.element_content += "</#{self.data}>"
|
220
|
+
@start_tag = 0
|
221
|
+
self.data = ""
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
else
|
226
|
+
if @start_tag == 0 && ESCAPED_TAGS.include?(self.element_name)
|
227
|
+
self.data += self.escape_char(current_char)
|
228
|
+
else
|
229
|
+
self.data += current_char
|
230
|
+
end
|
231
|
+
end
|
232
|
+
return true
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module WikiCloth
|
2
|
+
|
3
|
+
class WikiBuffer::Link < WikiBuffer
|
4
|
+
|
5
|
+
def initialize(data="",options={})
|
6
|
+
super(data,options)
|
7
|
+
@in_quotes = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def internal_link
|
11
|
+
@internal_link ||= false
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
link_handler = @options[:link_handler]
|
16
|
+
unless self.internal_link
|
17
|
+
return link_handler.external_link("#{params[0]}".strip, "#{params[1]}".strip)
|
18
|
+
else
|
19
|
+
case
|
20
|
+
when params[0] =~ /^:(.*)/
|
21
|
+
return link_handler.link_for(params[0],params[1])
|
22
|
+
when params[0] =~ /^\s*([a-zA-Z0-9-]+)\s*:(.*)$/
|
23
|
+
return link_handler.link_for_resource($1,$2,params[1..-1])
|
24
|
+
else
|
25
|
+
return link_handler.link_for(params[0],params[1])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
def internal_link=(val)
|
32
|
+
@internal_link = (val == true ? true : false)
|
33
|
+
end
|
34
|
+
|
35
|
+
def new_char()
|
36
|
+
case
|
37
|
+
# check if this link is internal or external
|
38
|
+
when previous_char.blank? && current_char == '['
|
39
|
+
self.internal_link = true
|
40
|
+
|
41
|
+
# Marks the beginning of another paramater for
|
42
|
+
# the current object
|
43
|
+
when current_char == '|' && self.internal_link == true && @in_quotes == false
|
44
|
+
self.current_param = self.data
|
45
|
+
self.data = ""
|
46
|
+
self.params << ""
|
47
|
+
|
48
|
+
# URL label
|
49
|
+
when current_char == ' ' && self.internal_link == false && params[1].nil? && !self.data.blank?
|
50
|
+
self.current_param = self.data
|
51
|
+
self.data = ""
|
52
|
+
self.params << ""
|
53
|
+
|
54
|
+
# end of link
|
55
|
+
when current_char == ']' && ((previous_char == ']' && self.internal_link == true) || self.internal_link == false) && @in_quotes == false
|
56
|
+
self.data.chop! if self.internal_link == true
|
57
|
+
self.current_param = self.data
|
58
|
+
self.data = ""
|
59
|
+
return false
|
60
|
+
|
61
|
+
else
|
62
|
+
self.data += current_char unless current_char == ' ' && self.data.blank?
|
63
|
+
end
|
64
|
+
|
65
|
+
return true
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
module WikiCloth
|
2
|
+
|
3
|
+
class WikiBuffer::Table < WikiBuffer
|
4
|
+
|
5
|
+
def initialize(data="",options={})
|
6
|
+
super(data,options)
|
7
|
+
self.buffer_type = "table"
|
8
|
+
@start_table = true
|
9
|
+
@start_row = false
|
10
|
+
@start_caption = false
|
11
|
+
@in_quotes = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def table_caption
|
15
|
+
@caption ||= ""
|
16
|
+
return @caption.kind_of?(Hash) ? @caption[:value] : @caption
|
17
|
+
end
|
18
|
+
|
19
|
+
def table_caption_attributes
|
20
|
+
@caption.kind_of?(Hash) ? @caption[:style] : ""
|
21
|
+
end
|
22
|
+
|
23
|
+
def rows
|
24
|
+
@rows ||= [ [] ]
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
row_count = 0
|
29
|
+
ret = "<table" + (params[0].blank? ? "" : " #{params[0].strip}") + ">"
|
30
|
+
ret += "<caption" + (self.table_caption_attributes.blank? ? "" : " #{table_caption_attributes.strip}") +
|
31
|
+
">#{table_caption.strip}</caption>" unless self.table_caption.blank?
|
32
|
+
for row in rows
|
33
|
+
row_count += 1
|
34
|
+
ret += "<tr" + (params[row_count].nil? || params[row_count].blank? ? "" : " #{params[row_count].strip}") + ">"
|
35
|
+
for cell in row
|
36
|
+
cell_attributes = cell[:style].blank? ? "" : " #{cell[:style].strip}"
|
37
|
+
ret += "<#{cell[:type]}#{cell_attributes}>\n#{cell[:value].strip}\n</#{cell[:type]}>"
|
38
|
+
end
|
39
|
+
ret += "</tr>"
|
40
|
+
end
|
41
|
+
ret += "</table>"
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
def rows=(val)
|
46
|
+
@rows = val
|
47
|
+
end
|
48
|
+
|
49
|
+
def table_caption_attributes=(val)
|
50
|
+
@caption = { :style => val, :value => self.table_caption } unless @caption.kind_of?(Hash)
|
51
|
+
@caption[:style] = val if @caption.kind_of?(Hash)
|
52
|
+
end
|
53
|
+
|
54
|
+
def table_caption=(val)
|
55
|
+
@caption = val unless @caption.kind_of?(Hash)
|
56
|
+
@caption[:value] = val if @caption.kind_of?(Hash)
|
57
|
+
end
|
58
|
+
|
59
|
+
def next_row()
|
60
|
+
self.params << ""
|
61
|
+
self.rows << []
|
62
|
+
end
|
63
|
+
|
64
|
+
def next_cell(type="td")
|
65
|
+
if self.rows[-1].size == 0
|
66
|
+
self.rows[-1] = [ { :type => type, :value => "", :style => "" } ]
|
67
|
+
else
|
68
|
+
self.rows[-1][-1][:value] = self.data
|
69
|
+
self.rows[-1] << { :type => type, :value => "", :style => "" }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def new_char()
|
74
|
+
if @check_cell_data == 1
|
75
|
+
case
|
76
|
+
when current_char != '|' && @start_caption == false && self.rows[-1][-1][:style].blank?
|
77
|
+
self.rows[-1][-1][:style] = self.data
|
78
|
+
self.data = ""
|
79
|
+
when current_char != '|' && @start_caption == true && self.table_caption_attributes.blank?
|
80
|
+
self.table_caption_attributes = self.data
|
81
|
+
self.data = ""
|
82
|
+
end
|
83
|
+
@check_cell_data = 0
|
84
|
+
end
|
85
|
+
|
86
|
+
case
|
87
|
+
# Next table cell in row (TD)
|
88
|
+
when current_char == "|" && (previous_char == "\n" || previous_char == "|") && @in_quotes == false
|
89
|
+
self.data.chop!
|
90
|
+
self.next_cell() unless self.data.blank? && previous_char == "|"
|
91
|
+
self.data = ""
|
92
|
+
|
93
|
+
# Next table cell in row (TH)
|
94
|
+
when current_char == "!" && (previous_char == "\n" || previous_char == "!") && @in_quotes == false
|
95
|
+
self.data.chop!
|
96
|
+
self.next_cell('th')
|
97
|
+
self.data = ""
|
98
|
+
|
99
|
+
# End of a table
|
100
|
+
when current_char == '}' && previous_char == '|'
|
101
|
+
self.data = ""
|
102
|
+
self.rows[-1].pop
|
103
|
+
return false
|
104
|
+
|
105
|
+
# Start table caption
|
106
|
+
when current_char == '+' && previous_char == '|' && @in_quotes == false
|
107
|
+
self.data = ""
|
108
|
+
self.rows[-1].pop
|
109
|
+
@start_caption = true
|
110
|
+
|
111
|
+
# Table cell might have attributes
|
112
|
+
when current_char == '|' && previous_char != "\n" && @in_quotes == false
|
113
|
+
@check_cell_data = 1
|
114
|
+
|
115
|
+
# End table caption
|
116
|
+
when current_char == "\n" && @start_caption == true && @in_quotes == false
|
117
|
+
@start_caption = false
|
118
|
+
self.table_caption = self.data
|
119
|
+
self.data = ""
|
120
|
+
|
121
|
+
# in quotes
|
122
|
+
when current_char == '"' && previous_char != '\\'
|
123
|
+
@in_quotes = !@in_quotes
|
124
|
+
self.data += '"'
|
125
|
+
|
126
|
+
# Table params
|
127
|
+
when current_char == "\n" && @start_table == true && @in_quotes == false
|
128
|
+
@start_table = false
|
129
|
+
unless self.data.blank?
|
130
|
+
self.current_param = self.data
|
131
|
+
self.params << ""
|
132
|
+
end
|
133
|
+
self.data = ""
|
134
|
+
|
135
|
+
# Table row params
|
136
|
+
when current_char == "\n" && @start_row == true && @in_quotes == false
|
137
|
+
@start_row = false
|
138
|
+
unless self.data.blank?
|
139
|
+
self.current_param = self.data
|
140
|
+
end
|
141
|
+
self.data = ""
|
142
|
+
|
143
|
+
# Start new table row
|
144
|
+
when current_char == '-' && previous_char == '|' && @in_quotes == false
|
145
|
+
self.data.chop!
|
146
|
+
self.rows[-1].pop
|
147
|
+
self.next_row()
|
148
|
+
@start_row = true
|
149
|
+
|
150
|
+
else
|
151
|
+
self.data += current_char
|
152
|
+
end
|
153
|
+
|
154
|
+
return true
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|