kron4eg-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 +0 -0
- data/Rakefile +23 -0
- data/init.rb +1 -0
- data/lib/core_ext.rb +43 -0
- data/lib/wiki_buffer.rb +279 -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_cloth.rb +61 -0
- data/lib/wiki_link_handler.rb +138 -0
- data/lib/wikicloth.rb +5 -0
- data/rails/init.rb +1 -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 +77 -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
File without changes
|
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.dirname(__FILE__) + "/rails/init.rb"
|
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
|
data/lib/wiki_buffer.rb
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
module WikiCloth
|
2
|
+
class WikiBuffer
|
3
|
+
|
4
|
+
def initialize(data="",options={})
|
5
|
+
@options = options
|
6
|
+
self.data = data
|
7
|
+
self.buffer_type = nil
|
8
|
+
@section_count = 0
|
9
|
+
@buffers ||= [ ]
|
10
|
+
@buffers << self
|
11
|
+
@list_data = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_globals?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def skip_html?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def data
|
23
|
+
@data ||= ""
|
24
|
+
end
|
25
|
+
|
26
|
+
def params
|
27
|
+
@params ||= [ "" ]
|
28
|
+
end
|
29
|
+
|
30
|
+
def buffer_type
|
31
|
+
@buffer_type
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
"<p>" + self.params.join("\n") + "</p>"
|
36
|
+
end
|
37
|
+
|
38
|
+
def check_globals()
|
39
|
+
return false if self.class != WikiBuffer
|
40
|
+
|
41
|
+
if previous_char == "\n"
|
42
|
+
if @indent == @buffers[-1].object_id && current_char != " "
|
43
|
+
# close pre tag
|
44
|
+
cc_temp = current_char
|
45
|
+
"</pre>".each_char { |c| self.add_char(c) }
|
46
|
+
# get the parser back on the right track
|
47
|
+
"\n#{cc_temp}".each_char { |c| @buffers[-1].add_char(c) }
|
48
|
+
@indent = nil
|
49
|
+
return true
|
50
|
+
end
|
51
|
+
if current_char == " " && @indent.nil? && @buffers[-1].class != WikiBuffer::HTMLElement
|
52
|
+
"<pre> ".each_char { |c| @buffers[-1].add_char(c) }
|
53
|
+
@indent = @buffers[-1].object_id
|
54
|
+
return true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if @buffers[-1].run_globals?
|
59
|
+
# new html tag
|
60
|
+
if @check_new_tag == true && current_char =~ /([a-z])/ && !@buffers[-1].skip_html?
|
61
|
+
@buffers[-1].data.chop!
|
62
|
+
parent = @buffers[-1].element_name if @buffers[-1].class == WikiBuffer::HTMLElement
|
63
|
+
@buffers << WikiBuffer::HTMLElement.new("",@options,parent)
|
64
|
+
end
|
65
|
+
@check_new_tag = current_char == '<' ? true : false
|
66
|
+
|
67
|
+
# global
|
68
|
+
case
|
69
|
+
# start variable
|
70
|
+
when previous_char == '{' && current_char == '{'
|
71
|
+
@buffers[-1].data.chop!
|
72
|
+
@buffers << WikiBuffer::Var.new("",@options)
|
73
|
+
return true
|
74
|
+
|
75
|
+
# start link
|
76
|
+
when current_char == '[' && previous_char != '['
|
77
|
+
@buffers << WikiBuffer::Link.new("",@options)
|
78
|
+
return true
|
79
|
+
|
80
|
+
# start table
|
81
|
+
when previous_char == '{' && current_char == "|"
|
82
|
+
@buffers[-1].data.chop!
|
83
|
+
@buffers << WikiBuffer::Table.new("",@options)
|
84
|
+
return true
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
return false
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_char(c)
|
93
|
+
self.previous_char = self.current_char
|
94
|
+
self.current_char = c
|
95
|
+
|
96
|
+
if self.check_globals() == false
|
97
|
+
case
|
98
|
+
when @buffers.size == 1
|
99
|
+
return self.new_char()
|
100
|
+
when @buffers[-1].add_char(c) == false && self.class == WikiBuffer
|
101
|
+
tmp = @buffers.pop
|
102
|
+
@buffers[-1].data += tmp.to_s
|
103
|
+
# any data left in the buffer we feed into the parent
|
104
|
+
unless tmp.data.blank?
|
105
|
+
tmp.data.each_char { |c| self.add_char(c) }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
# only executed in the default state
|
113
|
+
def new_char()
|
114
|
+
case
|
115
|
+
when current_char == "\n"
|
116
|
+
if @options[:extended_markup] == true
|
117
|
+
self.data.gsub!(/---([^-]+)---/,"<strike>\\1</strike>")
|
118
|
+
self.data.gsub!(/_([^_]+)_/,"<u>\\1</u>")
|
119
|
+
end
|
120
|
+
self.data.gsub!(/__([a-zA-Z0-9]+)__/) { |r|
|
121
|
+
case $1
|
122
|
+
when "NOEDITSECTION"
|
123
|
+
@noeditsection = true
|
124
|
+
end
|
125
|
+
""
|
126
|
+
}
|
127
|
+
self.data.gsub!(/^([-]{4,})/) { |r| "<hr />" }
|
128
|
+
self.data.gsub!(/^([=]{1,6})\s*(.*?)\s*(\1)/) { |r|
|
129
|
+
@section_count += 1
|
130
|
+
"<h#{$1.length}>" + (@noeditsection == true ? "" :
|
131
|
+
"<span class=\"editsection\">[<a href=\"" + @options[:link_handler].section_link(@section_count) +
|
132
|
+
"\" title=\"Edit section: #{$2}\">edit</a>]</span>") +
|
133
|
+
" <span class=\"mw-headline\">#{$2}</span></h#{$1.length}>"
|
134
|
+
}
|
135
|
+
self.data.gsub!(/([\']{2,5})(.*?)(\1)/) { |r|
|
136
|
+
tmp = "<i>#{$2}</i>" if $1.length == 2
|
137
|
+
tmp = "<b>#{$2}</b>" if $1.length == 3
|
138
|
+
tmp = "<b>'#{$2}'</b>" if $1.length == 4
|
139
|
+
tmp = "<b><i>#{$2}</i></b>" if $1.length == 5
|
140
|
+
tmp
|
141
|
+
}
|
142
|
+
lines = self.data.split("\n")
|
143
|
+
self.data = ""
|
144
|
+
for line in lines
|
145
|
+
if !@list_data.empty? && (line.blank? || line =~ /^([^#\*:;]+)/)
|
146
|
+
tmp = ""
|
147
|
+
@list_data.reverse!
|
148
|
+
@list_data.each { |x| tmp += "</" + list_inner_tag_for(x) + "></#{list_tag_for(x)}>" }
|
149
|
+
line = "#{tmp} #{line}"
|
150
|
+
@list_data = []
|
151
|
+
end
|
152
|
+
line.gsub!(/^([#\*:;]+)(.*)$/) { |r|
|
153
|
+
cdata = []
|
154
|
+
tmp = ""
|
155
|
+
$1.each_char { |c| cdata << c }
|
156
|
+
if @list_data.empty?
|
157
|
+
tmp += "<#{list_tag_for(cdata[0])}>"
|
158
|
+
cdata[1..-1].each { |x| tmp += "<" + list_inner_tag_for(cdata[0]) + "><#{list_tag_for(x)}>" } if cdata.size > 1
|
159
|
+
else
|
160
|
+
case
|
161
|
+
when cdata.size > @list_data.size
|
162
|
+
i = cdata.size-@list_data.size
|
163
|
+
cdata[-i,i].each { |x| tmp += "<#{list_tag_for(x)}>" }
|
164
|
+
when cdata.size < @list_data.size
|
165
|
+
i = @list_data.size-cdata.size
|
166
|
+
nlist = @list_data[-i,i].reverse
|
167
|
+
nlist.each { |x| tmp += "</" + list_inner_tag_for(x) + "></#{list_tag_for(x)}>" }
|
168
|
+
tmp += "</#{list_inner_tag_for(cdata.last)}>"
|
169
|
+
else
|
170
|
+
if cdata != @list_data
|
171
|
+
# FIXME: this will only work if the change depth is one level
|
172
|
+
unless (@list_data.last == ';' || @list_data.last == ':') && (cdata.last == ';' || cdata.last == ':')
|
173
|
+
tmp += "</#{list_tag_for(@list_data.pop)}>"
|
174
|
+
tmp += "<#{list_tag_for(cdata.last)}>"
|
175
|
+
end
|
176
|
+
else
|
177
|
+
tmp += "</" + list_inner_tag_for(@list_data.last) + ">"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
# FIXME: still probably does not detect the : properly
|
182
|
+
peices = cdata.last == ";" ? $2.smart_split(":") : [ $2 ]
|
183
|
+
if peices.size > 1
|
184
|
+
tmp += "<#{list_inner_tag_for(cdata.last)}>#{peices[0]}</#{list_inner_tag_for(cdata.last)}>"
|
185
|
+
tmp += "<dd>#{peices[1..-1].join(":")}</dd>"
|
186
|
+
cdata[-1] = ":"
|
187
|
+
else
|
188
|
+
tmp += "<#{list_inner_tag_for(cdata.last)}>#{peices[0]}"
|
189
|
+
end
|
190
|
+
@list_data = cdata
|
191
|
+
tmp
|
192
|
+
}
|
193
|
+
self.data += line + "\n"
|
194
|
+
end
|
195
|
+
|
196
|
+
self.data = "</p><p>" if self.data.blank?
|
197
|
+
|
198
|
+
self.params << self.data.auto_link
|
199
|
+
self.data = ""
|
200
|
+
else
|
201
|
+
self.data += current_char
|
202
|
+
end
|
203
|
+
return true
|
204
|
+
end
|
205
|
+
|
206
|
+
def name_current_param()
|
207
|
+
params[-1] = { :value => "", :name => params[-1] } unless params[-1].kind_of?(Hash) || params[-1].nil?
|
208
|
+
end
|
209
|
+
|
210
|
+
def current_param=(val)
|
211
|
+
unless self.params[-1].nil? || self.params[-1].kind_of?(String)
|
212
|
+
self.params[-1][:value] = val
|
213
|
+
else
|
214
|
+
self.params[-1] = val
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def params=(val)
|
219
|
+
@params = val
|
220
|
+
end
|
221
|
+
|
222
|
+
def buffer_type=(val)
|
223
|
+
@buffer_type = val
|
224
|
+
end
|
225
|
+
|
226
|
+
def data=(val)
|
227
|
+
@data = val
|
228
|
+
end
|
229
|
+
|
230
|
+
def current_char=(val)
|
231
|
+
@current_char = val
|
232
|
+
end
|
233
|
+
|
234
|
+
def current_char
|
235
|
+
@current_char ||= ""
|
236
|
+
end
|
237
|
+
|
238
|
+
def previous_char=(val)
|
239
|
+
@previous_char = val
|
240
|
+
end
|
241
|
+
|
242
|
+
def previous_char
|
243
|
+
@previous_char
|
244
|
+
end
|
245
|
+
|
246
|
+
def current_line=(val)
|
247
|
+
@current_line = val
|
248
|
+
end
|
249
|
+
|
250
|
+
def current_line
|
251
|
+
@current_line ||= ""
|
252
|
+
end
|
253
|
+
|
254
|
+
def list_tag_for(tag)
|
255
|
+
case tag
|
256
|
+
when "#" then "ol"
|
257
|
+
when "*" then "ul"
|
258
|
+
when ";" then "dl"
|
259
|
+
when ":" then "dl"
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def list_inner_tag_for(tag)
|
264
|
+
case tag
|
265
|
+
when "#" then "li"
|
266
|
+
when "*" then "li"
|
267
|
+
when ";" then "dt"
|
268
|
+
when ":" then "dd"
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
|
276
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "wiki_buffer", "html_element")
|
277
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "wiki_buffer", "table")
|
278
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "wiki_buffer", "var")
|
279
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "wiki_buffer", "link")
|
@@ -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
|