wikicloth 0.1.3

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/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>&#123;&#123;test&#125;&#125; <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('<','&lt;').gsub('>','&gt;')
38
+ # hack to fix <code><nowiki> nested mess
39
+ self.element_content = self.element_content.gsub(/&lt;[\/]*\s*nowiki\s*&gt;/,'')
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('<', '&lt;').gsub('>', '&gt;')
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 '&#45;'
108
+ when '!' then '&#33;'
109
+ when '|' then '&#124;'
110
+ when '&' then '&amp;'
111
+ when '"' then '&quot;'
112
+ when '{' then '&#123;'
113
+ when '}' then '&#125;'
114
+ when '[' then '&#91;'
115
+ when ']' then '&#93;'
116
+ when '*' then '&#42;'
117
+ when '#' then '&#35;'
118
+ when ':' then '&#58;'
119
+ when ';' then '&#59;'
120
+ when "'" then '&#39;'
121
+ when '=' then '&#61;'
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 += "&lt;/#{self.data}&gt;"
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