ruby-bbcode 0.0.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +7 -0
- data/README.textile +12 -6
- data/Rakefile +10 -2
- data/lib/ruby-bbcode.rb +55 -178
- data/lib/ruby-bbcode/bbtree.rb +94 -0
- data/lib/ruby-bbcode/tag_collection.rb +99 -0
- data/lib/ruby-bbcode/tag_info.rb +132 -0
- data/lib/ruby-bbcode/tag_node.rb +71 -0
- data/lib/ruby-bbcode/tag_sifter.rb +329 -0
- data/lib/ruby-bbcode/version.rb +5 -2
- data/lib/tags/tags.rb +45 -12
- data/test/current_test.rb +8 -0
- data/test/debugging.rb +93 -0
- data/test/ruby_bbcode_test.rb +107 -32
- data/test/test_helper.rb +16 -5
- data/test/unit/debugging_test.rb +48 -0
- data/test/unit/tag_sifter_test.rb +51 -0
- metadata +67 -22
- data/test/dummy/config/application.rb +0 -14
- data/test/dummy/config/boot.rb +0 -10
- data/test/dummy/config/environment.rb +0 -15
- data/test/dummy/config/environments/test.rb +0 -39
data/CHANGELOG.md
ADDED
data/README.textile
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
h1. Ruby-
|
1
|
+
h1. Ruby-BBCode
|
2
|
+
|
3
|
+
!https://badge.fury.io/rb/ruby-bbcode.svg!:http://badge.fury.io/rb/ruby-bbcode
|
2
4
|
|
3
5
|
This gem adds support for "BBCode":http:/www.bbcode.org/ to Ruby. The BBCode is parsed by a parser before converted to HTML, allowing to convert nested BBCode tags in strings to their correct HTML equivalent. The parser also checks whether the BBCode is valid and gives errors for incorrect BBCode texts.
|
4
6
|
|
@@ -11,21 +13,25 @@ h2. Example
|
|
11
13
|
|
12
14
|
h2. Installing
|
13
15
|
|
14
|
-
Add the folling line to the Gemfile
|
16
|
+
Add the folling line to the Gemfile of your application:
|
15
17
|
<pre><code>gem 'ruby-bbcode'</code></pre>
|
16
|
-
|
18
|
+
|
19
|
+
Or to use the source code from the repository:
|
17
20
|
<pre><code>gem 'ruby-bbcode', :git => 'git://github.com/veger/ruby-bbcode.git'</code></pre>
|
21
|
+
|
18
22
|
Run
|
19
23
|
<pre><code>bundle install</code></pre>
|
20
24
|
|
21
|
-
And
|
25
|
+
And Ruby-BBCode is available in your application.
|
22
26
|
|
23
27
|
_Note_: Do not forget to restart your server!
|
24
28
|
|
25
29
|
h2. Acknowledgements
|
26
30
|
|
27
|
-
|
31
|
+
A big thanks to "TheNotary":https://github.com/TheNotary for all contributions he made to this project!
|
32
|
+
|
33
|
+
Some of the ideas and the tests came from "bb-ruby":http://github.com/cpjolicoeur/bb-ruby of Craig P Jolicoeur.
|
28
34
|
|
29
35
|
h2. License
|
30
36
|
|
31
|
-
MIT License. See the included MIT-LICENCE file.
|
37
|
+
MIT License. See the included "MIT-LICENCE":https://github.com/veger/ruby-bbcode/blob/master/MIT-LICENSE file.
|
data/Rakefile
CHANGED
@@ -9,14 +9,16 @@ begin
|
|
9
9
|
rescue LoadError
|
10
10
|
require 'rdoc/rdoc'
|
11
11
|
require 'rake/rdoctask'
|
12
|
+
require 'rdoc/task'
|
12
13
|
RDoc::Task = Rake::RDocTask
|
13
14
|
end
|
14
15
|
|
15
16
|
RDoc::Task.new(:rdoc) do |rdoc|
|
16
17
|
rdoc.rdoc_dir = 'rdoc'
|
17
|
-
rdoc.title = '
|
18
|
+
rdoc.title = 'Ruby BBCode'
|
18
19
|
rdoc.options << '--line-numbers'
|
19
|
-
rdoc.rdoc_files.include('README.
|
20
|
+
rdoc.rdoc_files.include('README.textile')
|
21
|
+
rdoc.rdoc_files.include('CHANGELOG.md')
|
20
22
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
23
|
end
|
22
24
|
|
@@ -33,5 +35,11 @@ Rake::TestTask.new(:test) do |t|
|
|
33
35
|
t.verbose = true
|
34
36
|
end
|
35
37
|
|
38
|
+
Rake::TestTask.new(:current) do |t|
|
39
|
+
t.libs << 'lib'
|
40
|
+
t.libs << 'test'
|
41
|
+
t.pattern = 'test/**/current_test.rb'
|
42
|
+
t.verbose = true
|
43
|
+
end
|
36
44
|
|
37
45
|
task :default => :test
|
data/lib/ruby-bbcode.rb
CHANGED
@@ -1,200 +1,77 @@
|
|
1
1
|
require 'tags/tags'
|
2
|
-
|
2
|
+
require 'ruby-bbcode/tag_info'
|
3
|
+
require 'ruby-bbcode/tag_sifter'
|
4
|
+
require 'ruby-bbcode/tag_node'
|
5
|
+
require 'ruby-bbcode/tag_collection'
|
6
|
+
require 'ruby-bbcode/bbtree'
|
7
|
+
|
8
|
+
# RubyBBCode adds support for BBCode to Ruby.
|
9
|
+
# The BBCode is parsed by a parser before converted to HTML, allowing to convert nested BBCode tags in strings to their correct HTML equivalent.
|
10
|
+
# THe used parser also checks whether the BBCode is valid and gives errors for incorrect BBCode texts.
|
3
11
|
module RubyBBCode
|
4
|
-
include
|
5
|
-
|
6
|
-
@@to_sentence_bbcode_tags = {:words_connector => "], [",
|
7
|
-
:two_words_connector => "] and [",
|
8
|
-
:last_word_connector => "] and ["}
|
12
|
+
include ::RubyBBCode::Tags
|
9
13
|
|
14
|
+
# This method converts the given text (with BBCode tags) into a HTML representation
|
15
|
+
# The escape_html parameter (default: true) escapes HTML tags that were present in the given text and therefore blocking (mallicious) HTML in the original text
|
16
|
+
# The additional_tags parameter is used to add additional BBCode tags that should be accepted
|
17
|
+
# The method paramter determines whether the tags parameter needs to be used to blacklist (when set to :disable) or whitelist (when not set to :disable) the list of BBCode tags
|
10
18
|
def self.to_html(text, escape_html = true, additional_tags = {}, method = :disable, *tags)
|
11
|
-
# We cannot convert to HTML if the BBCode is not valid!
|
12
19
|
text = text.clone
|
13
|
-
use_tags = @@tags.merge(additional_tags)
|
14
20
|
|
15
|
-
|
16
|
-
tags.each { |t| use_tags.delete(t) }
|
17
|
-
else
|
18
|
-
new_use_tags = {}
|
19
|
-
tags.each { |t| new_use_tags[t] = use_tags[t] if use_tags.key?(t) }
|
20
|
-
use_tags = new_use_tags
|
21
|
-
end
|
21
|
+
use_tags = determine_applicable_tags(additional_tags, method, *tags)
|
22
22
|
|
23
|
-
|
24
|
-
text.gsub!('<', '<')
|
25
|
-
text.gsub!('>', '>')
|
26
|
-
end
|
23
|
+
@tag_sifter = TagSifter.new(text, use_tags, escape_html)
|
27
24
|
|
28
|
-
|
29
|
-
raise valid.join(', ') if valid != true
|
25
|
+
@tag_sifter.process_text
|
30
26
|
|
31
|
-
|
32
|
-
|
27
|
+
if @tag_sifter.invalid?
|
28
|
+
raise @tag_sifter.errors.join(', ') # We cannot convert to HTML if the BBCode is not valid!
|
29
|
+
else
|
30
|
+
@tag_sifter.bbtree.to_html(use_tags)
|
31
|
+
end
|
33
32
|
|
34
|
-
def self.is_valid?(text, additional_tags = {})
|
35
|
-
parse(text, @@tags.merge(additional_tags));
|
36
33
|
end
|
37
34
|
|
38
|
-
|
39
|
-
|
35
|
+
# Returns true when valid, else returns array with error(s)
|
36
|
+
def self.validity_check(text, additional_tags = {})
|
37
|
+
@tag_sifter = TagSifter.new(text, @@tags.merge(additional_tags))
|
38
|
+
|
39
|
+
@tag_sifter.process_text
|
40
|
+
return @tag_sifter.errors if @tag_sifter.invalid?
|
41
|
+
true
|
40
42
|
end
|
41
43
|
|
44
|
+
|
42
45
|
protected
|
43
|
-
def self.parse(text, tags = {})
|
44
|
-
tags = @@tags if tags == {}
|
45
|
-
tags_list = []
|
46
|
-
@bbtree = {:nodes => []}
|
47
|
-
bbtree_depth = 0
|
48
|
-
bbtree_current_node = @bbtree
|
49
|
-
text.scan(/((\[ (\/)? (\w+) ((=[^\[\]]+) | (\s\w+=\w+)* | ([^\]]*))? \]) | ([^\[]+))/ix) do |tag_info|
|
50
|
-
ti = find_tag_info(tag_info)
|
51
|
-
|
52
|
-
if ti[:is_tag] and !tags.include?(ti[:tag].to_sym)
|
53
|
-
# Handle as text from now on!
|
54
|
-
ti[:is_tag] = false
|
55
|
-
ti[:text] = ti[:complete_match]
|
56
|
-
end
|
57
|
-
|
58
|
-
if !ti[:is_tag] or !ti[:closing_tag]
|
59
|
-
if ti[:is_tag]
|
60
|
-
tag = tags[ti[:tag].to_sym]
|
61
|
-
unless tag[:only_in].nil? or (tags_list.length > 0 and tag[:only_in].include?(tags_list.last.to_sym))
|
62
|
-
# Tag does to be put in the last opened tag
|
63
|
-
err = "[#{ti[:tag]}] can only be used in [#{tag[:only_in].to_sentence(@@to_sentence_bbcode_tags)}]"
|
64
|
-
err += ", so using it in a [#{tags_list.last}] tag is not allowed" if tags_list.length > 0
|
65
|
-
return [err]
|
66
|
-
end
|
67
|
-
|
68
|
-
if tag[:allow_tag_param] and ti[:params][:tag_param] != nil
|
69
|
-
# Test if matches
|
70
|
-
return [tag[:tag_param_description].gsub('%param%', ti[:params][:tag_param])] if ti[:params][:tag_param].match(tag[:tag_param]).nil?
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
if tags_list.length > 0 and tags[tags_list.last.to_sym][:only_allow] != nil
|
75
|
-
# Check if the found tag is allowed
|
76
|
-
last_tag = tags[tags_list.last.to_sym]
|
77
|
-
allowed_tags = last_tag[:only_allow]
|
78
|
-
if (!ti[:is_tag] and last_tag[:require_between] != true and ti[:text].lstrip != "") or (ti[:is_tag] and (allowed_tags.include?(ti[:tag].to_sym) == false))
|
79
|
-
# Last opened tag does not allow tag
|
80
|
-
err = "[#{tags_list.last}] can only contain [#{allowed_tags.to_sentence(@@to_sentence_bbcode_tags)}] tags, so "
|
81
|
-
err += "[#{ti[:tag]}]" if ti[:is_tag]
|
82
|
-
err += "\"#{ti[:text]}\"" unless ti[:is_tag]
|
83
|
-
err += ' is not allowed'
|
84
|
-
return [err]
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Validation of tag succeeded, add to tags_list and/or bbtree
|
89
|
-
if ti[:is_tag]
|
90
|
-
tag = tags[ti[:tag].to_sym]
|
91
|
-
tags_list.push ti[:tag]
|
92
|
-
element = {:is_tag => true, :tag => ti[:tag].to_sym, :nodes => [] }
|
93
|
-
element[:params] = {:tag_param => ti[:params][:tag_param]} if tag[:allow_tag_param] and ti[:params][:tag_param] != nil
|
94
|
-
else
|
95
|
-
text = ti[:text]
|
96
|
-
text.gsub!("\r\n", "\n")
|
97
|
-
text.gsub!("\n", "<br />\n")
|
98
|
-
element = {:is_tag => false, :text => text }
|
99
|
-
if bbtree_depth > 0
|
100
|
-
tag = tags[bbtree_current_node[:tag]]
|
101
|
-
if tag[:require_between] == true
|
102
|
-
bbtree_current_node[:between] = ti[:text]
|
103
|
-
if tag[:allow_tag_param] and tag[:allow_tag_param_between] and (bbtree_current_node[:params] == nil or bbtree_current_node[:params][:tag_param] == nil)
|
104
|
-
# Did not specify tag_param, so use between.
|
105
|
-
# Check if valid
|
106
|
-
return [tag[:tag_param_description].gsub('%param%', ti[:text])] if ti[:text].match(tag[:tag_param]).nil?
|
107
|
-
# Store as tag_param
|
108
|
-
bbtree_current_node[:params] = {:tag_param => ti[:text]}
|
109
|
-
end
|
110
|
-
element = nil
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
bbtree_current_node[:nodes] << element unless element == nil
|
115
|
-
if ti[:is_tag]
|
116
|
-
# Advance to next level (the node we just added)
|
117
|
-
bbtree_current_node = element
|
118
|
-
bbtree_depth += 1
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
if ti[:is_tag] and ti[:closing_tag]
|
123
|
-
if ti[:is_tag]
|
124
|
-
tag = tags[ti[:tag].to_sym]
|
125
|
-
return ["Closing tag [/#{ti[:tag]}] does match [#{tags_list.last}]"] if tags_list.last != ti[:tag]
|
126
|
-
return ["No text between [#{ti[:tag]}] and [/#{ti[:tag]}] tags."] if tag[:require_between] == true and bbtree_current_node[:between].blank?
|
127
|
-
tags_list.pop
|
128
|
-
|
129
|
-
# Find parent node (kinda hard since no link to parent node is available...)
|
130
|
-
bbtree_depth -= 1
|
131
|
-
bbtree_current_node = @bbtree
|
132
|
-
bbtree_depth.times { bbtree_current_node = bbtree_current_node[:nodes].last }
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
return ["[#{tags_list.to_sentence((@@to_sentence_bbcode_tags))}] not closed"] if tags_list.length > 0
|
137
46
|
|
138
|
-
|
47
|
+
# This method provides the final set of bbcode tags, it merges the default tags with the given additional_tags
|
48
|
+
# and blacklists(method = :disable) or whitelists the list of tags with the given tags parameter.
|
49
|
+
def self.determine_applicable_tags(additional_tags, method, *tags)
|
50
|
+
use_tags = @@tags.merge(additional_tags)
|
51
|
+
if method == :disable then # if method is set to :disable
|
52
|
+
tags.each { |t| use_tags.delete(t) } # blacklist (remove) the supplied tags
|
53
|
+
else # method is not :disable, but has any other value
|
54
|
+
# Only use the supplied tags (whitelist)
|
55
|
+
new_use_tags = {}
|
56
|
+
tags.each { |t| new_use_tags[t] = use_tags[t] if use_tags.key?(t) }
|
57
|
+
use_tags = new_use_tags
|
58
|
+
end
|
59
|
+
use_tags
|
139
60
|
end
|
140
61
|
|
141
|
-
def self.
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
ti[:params] = {}
|
149
|
-
if tag_info[4][0] == ?=
|
150
|
-
ti[:params][:tag_param] = tag_info[4][1..-1]
|
151
|
-
elsif tag_info[4][0] == ?\s
|
152
|
-
#TODO: Find params
|
153
|
-
end
|
62
|
+
def self.parse(text, tags)
|
63
|
+
@tag_sifter = TagSifter.new(text, tags)
|
64
|
+
|
65
|
+
@tag_sifter.process_text
|
66
|
+
|
67
|
+
if @tag_sifter.invalid?
|
68
|
+
@tag_sifter.errors
|
154
69
|
else
|
155
|
-
|
156
|
-
ti[:text] = tag_info[8]
|
70
|
+
true
|
157
71
|
end
|
158
|
-
ti
|
159
|
-
end
|
160
72
|
|
161
|
-
def self.bbtree_to_html(node_list, tags = {})
|
162
|
-
tags = @@tags if tags == {}
|
163
|
-
text = ""
|
164
|
-
node_list.each do |node|
|
165
|
-
if node[:is_tag]
|
166
|
-
tag = tags[node[:tag]]
|
167
|
-
t = tag[:html_open].dup
|
168
|
-
t.gsub!('%between%', node[:between]) if tag[:require_between]
|
169
|
-
if tag[:allow_tag_param]
|
170
|
-
if node[:params] and !node[:params][:tag_param].blank?
|
171
|
-
match_array = node[:params][:tag_param].scan(tag[:tag_param])[0]
|
172
|
-
index = 0
|
173
|
-
match_array.each do |match|
|
174
|
-
if index < tag[:tag_param_tokens].length
|
175
|
-
t.gsub!("%#{tag[:tag_param_tokens][index][:token].to_s}%", tag[:tag_param_tokens][index][:prefix].to_s+match+tag[:tag_param_tokens][index][:postfix].to_s)
|
176
|
-
index += 1
|
177
|
-
end
|
178
|
-
end
|
179
|
-
else
|
180
|
-
# Remove unused tokens
|
181
|
-
tag[:tag_param_tokens].each do |token|
|
182
|
-
t.gsub!("%#{token[:token]}%", '')
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
text += t
|
188
|
-
text += bbtree_to_html(node[:nodes], tags) if node[:nodes].length > 0
|
189
|
-
t = tag[:html_close]
|
190
|
-
t.gsub!('%between%', node[:between]) if tag[:require_between]
|
191
|
-
text += t
|
192
|
-
else
|
193
|
-
text += node[:text] unless node[:text].nil?
|
194
|
-
end
|
195
|
-
end
|
196
|
-
text
|
197
73
|
end
|
74
|
+
|
198
75
|
end
|
199
76
|
|
200
77
|
String.class_eval do
|
@@ -202,14 +79,14 @@ String.class_eval do
|
|
202
79
|
def bbcode_to_html(escape_html = true, additional_tags = {}, method = :disable, *tags)
|
203
80
|
RubyBBCode.to_html(self, escape_html, additional_tags, method, *tags)
|
204
81
|
end
|
205
|
-
|
82
|
+
|
206
83
|
# Replace the BBCode content of a string with its corresponding HTML markup
|
207
84
|
def bbcode_to_html!(escape_html = true, additional_tags = {}, method = :disable, *tags)
|
208
85
|
self.replace(RubyBBCode.to_html(self, escape_html, additional_tags, method, *tags))
|
209
86
|
end
|
210
87
|
|
211
88
|
# Check if string contains valid BBCode. Returns true when valid, else returns array with error(s)
|
212
|
-
def
|
213
|
-
RubyBBCode.
|
89
|
+
def bbcode_check_validity
|
90
|
+
RubyBBCode.validity_check(self)
|
214
91
|
end
|
215
92
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module RubyBBCode
|
2
|
+
# Tree of nodes containing the parsed BBCode information and the plain texts
|
3
|
+
#
|
4
|
+
# As you parse a string of text, say:
|
5
|
+
# "[b]I'm bold and the next word is [i]ITALIC[/i][b]"
|
6
|
+
# ...you build up a tree of nodes (@bbtree). The above string converts to 4 nodes when the parse has completed.
|
7
|
+
# * Node 1) An opening tag node representing "[b]"
|
8
|
+
# * Node 2) A text node representing "I'm bold and the next word is "
|
9
|
+
# * Node 3) An opening tag node representing "[i]"
|
10
|
+
# * Node 4) A text node representing "ITALIC"
|
11
|
+
#
|
12
|
+
# The closing of the nodes seems to be implied which is fine by me --less to keep track of.
|
13
|
+
#
|
14
|
+
class BBTree
|
15
|
+
attr_accessor :current_node, :tags_list
|
16
|
+
|
17
|
+
def initialize(hash = { :nodes => TagCollection.new }, dictionary)
|
18
|
+
@bbtree = hash
|
19
|
+
@current_node = TagNode.new(@bbtree)
|
20
|
+
@tags_list = []
|
21
|
+
@dictionary = dictionary
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](key)
|
25
|
+
@bbtree[key]
|
26
|
+
end
|
27
|
+
|
28
|
+
def []=(key, value)
|
29
|
+
@bbtree[key] = value
|
30
|
+
end
|
31
|
+
|
32
|
+
def nodes
|
33
|
+
@bbtree[:nodes]
|
34
|
+
end
|
35
|
+
alias :children :nodes # needed due to the similarities between BBTree[:nodes] and TagNode[:nodes]... they're walked through in debugging.rb right now
|
36
|
+
|
37
|
+
def type
|
38
|
+
:bbtree
|
39
|
+
end
|
40
|
+
|
41
|
+
def within_open_tag?
|
42
|
+
@tags_list.length > 0
|
43
|
+
end
|
44
|
+
alias :expecting_a_closing_tag? :within_open_tag? # just giving this method multiple names for semantical purposes
|
45
|
+
|
46
|
+
def parent_tag
|
47
|
+
return nil if !within_open_tag?
|
48
|
+
@tags_list.last.to_sym
|
49
|
+
end
|
50
|
+
|
51
|
+
def parent_has_constraints_on_children?
|
52
|
+
@dictionary[parent_tag][:only_allow] != nil
|
53
|
+
end
|
54
|
+
|
55
|
+
# Advance to next level (the node we just added)
|
56
|
+
def escalate_bbtree(element)
|
57
|
+
@tags_list.push element[:tag]
|
58
|
+
@current_node = TagNode.new(element)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Step down the bbtree a notch because we've reached a closing tag
|
62
|
+
def retrogress_bbtree
|
63
|
+
@tags_list.pop # remove latest tag in tags_list since it's closed now...
|
64
|
+
# The parsed data manifests in @bbtree.current_node.children << TagNode.new(element) which I think is more confusing than needed
|
65
|
+
|
66
|
+
if within_open_tag?
|
67
|
+
# Set the current node to be the node we've just parsed over which is infact within another node??...
|
68
|
+
@current_node = TagNode.new(self.nodes.last)
|
69
|
+
else # If we're still at the root of the BBTree or have returned back to the root via encountring closing tags...
|
70
|
+
@current_node = TagNode.new({:nodes => self.nodes}) # Note: just passing in self works too...
|
71
|
+
end
|
72
|
+
|
73
|
+
# OKOKOK!
|
74
|
+
# Since @bbtree = @current_node, if we ever set @current_node to something, we're actually changing @bbtree...
|
75
|
+
# therefore... my brain is now numb
|
76
|
+
end
|
77
|
+
|
78
|
+
def redefine_parent_tag_as_text
|
79
|
+
@tags_list.pop
|
80
|
+
@current_node[:is_tag] = false
|
81
|
+
@current_node[:closing_tag] = false
|
82
|
+
@current_node.element[:text] = "[#{@current_node[:tag].to_s}]"
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_up_new_tag(element)
|
86
|
+
@current_node.children << TagNode.new(element)
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_html(tags = {})
|
90
|
+
self.nodes.to_html(tags)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module RubyBBCode
|
2
|
+
# This class holds TagNode instances and helps build them into html when the time comes.
|
3
|
+
#
|
4
|
+
# It is really just a simple array, with the addition of the #to_html method
|
5
|
+
class TagCollection < Array
|
6
|
+
|
7
|
+
# This method is vulnerable to stack-level-too-deep scenarios where >=1,200 tags are being parsed.
|
8
|
+
# But that scenario can be mitigated by splitting up the tags. bbtree = { :nodes => [900tags, 1000tags] }, the work
|
9
|
+
# for that bbtree can be split up into two passes, do the each node one at a time. I'm not coding that though, it's pointless, just a thought though
|
10
|
+
def to_html(tags)
|
11
|
+
html_string = ""
|
12
|
+
self.each do |node|
|
13
|
+
if node.type == :tag
|
14
|
+
t = HtmlTemplate.new node
|
15
|
+
|
16
|
+
t.inlay_between_text!
|
17
|
+
|
18
|
+
if node.allow_tag_param? and node.param_set?
|
19
|
+
t.inlay_inline_params!
|
20
|
+
elsif node.allow_tag_param? and node.param_not_set?
|
21
|
+
t.remove_unused_tokens!
|
22
|
+
end
|
23
|
+
|
24
|
+
html_string << t.opening_html
|
25
|
+
|
26
|
+
# invoke "recursive" call if this node contains child nodes
|
27
|
+
html_string << node.children.to_html(tags) if node.has_children? # FIXME: Don't use recursion, it can lead to stack-level-too-deep errors for large volumes?
|
28
|
+
|
29
|
+
t.inlay_closing_html!
|
30
|
+
|
31
|
+
html_string << t.closing_html
|
32
|
+
elsif node.type == :text
|
33
|
+
html_string << node[:text] unless node[:text].nil?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
html_string
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
# This class is designed to help us build up the HTML data. It starts out as a template such as...
|
43
|
+
# @opening_html = '<a href="%url%">%between%'
|
44
|
+
# @closing_html = '</a>'
|
45
|
+
# and then slowly turns into...
|
46
|
+
# @opening_html = '<a href="http://www.blah.com">cool beans'
|
47
|
+
# @closing_html = '</a>'
|
48
|
+
# TODO: Think about creating a separate file for this or something... maybe look into folder structures cause this project
|
49
|
+
# got huge when I showed up.
|
50
|
+
class HtmlTemplate
|
51
|
+
attr_accessor :opening_html, :closing_html
|
52
|
+
|
53
|
+
def initialize(node)
|
54
|
+
@node = node
|
55
|
+
@tag_definition = node.definition # tag_definition
|
56
|
+
@opening_html = node.definition[:html_open].dup
|
57
|
+
@closing_html = node.definition[:html_close].dup
|
58
|
+
end
|
59
|
+
|
60
|
+
def inlay_between_text!
|
61
|
+
@opening_html.gsub!('%between%',@node[:between]) if between_text_goes_into_html_output_as_param? # set the between text to where it goes if required to do so...
|
62
|
+
end
|
63
|
+
|
64
|
+
def inlay_inline_params!
|
65
|
+
# Get list of paramaters to feed
|
66
|
+
match_array = @node[:params][:tag_param].scan(@tag_definition[:tag_param])[0]
|
67
|
+
|
68
|
+
# for each parameter to feed
|
69
|
+
match_array.each.with_index do |match, i|
|
70
|
+
if i < @tag_definition[:tag_param_tokens].length
|
71
|
+
|
72
|
+
# Substitute the %param% keyword for the appropriate data specified
|
73
|
+
@opening_html.gsub!("%#{@tag_definition[:tag_param_tokens][i][:token].to_s}%",
|
74
|
+
@tag_definition[:tag_param_tokens][i][:prefix].to_s +
|
75
|
+
match +
|
76
|
+
@tag_definition[:tag_param_tokens][i][:postfix].to_s)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def inlay_closing_html!
|
82
|
+
@closing_html.gsub!('%between%',@node[:between]) if @tag_definition[:require_between]
|
83
|
+
end
|
84
|
+
|
85
|
+
def remove_unused_tokens!
|
86
|
+
@tag_definition[:tag_param_tokens].each do |token|
|
87
|
+
@opening_html.gsub!("%#{token[:token]}%", '')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def between_text_goes_into_html_output_as_param?
|
94
|
+
@tag_definition[:require_between]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|