Module: BBCode

Defined in:
lib/bbcode-rails.rb,
lib/bbcode-rails/railtie.rb,
lib/bbcode-rails/version.rb

Defined Under Namespace

Classes: ParseError, Railtie, Tag

Constant Summary collapse

VERSION =
"0.9.4"

Class Method Summary collapse

Class Method Details

.get_tag_by_name(name) ⇒ Object



12
13
14
15
16
17
18
19
20
# File 'lib/bbcode-rails.rb', line 12

def self.get_tag_by_name name
  if defined?(Rails) && Rails.env.development?
    begin
      "#{name}_tag".camelize.constantize
    rescue NameError
    end
  end
  @tags["#{name.to_s.downcase}tag"]
end

.parse(str, raise_error = false) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/bbcode-rails.rb', line 22

def self.parse str, raise_error=false
  str = str.dup

  str.gsub!('&', '&')
  str.gsub!('<', '&lt;')
  str.gsub!('>', '&gt;')
  str.gsub!('"', '&quot;')
  str.gsub!("'", '&apos;')

  # Let's iterate over the pieces to build a tree
  # It works like this:
  # For each object you have two things:
  #   1. It is a tag name a la [img]
  #   2. It is a simple string, something like 'Hello'
  #
  # Now, we want a tree that looks like this
  #
  # -> |
  #    | ImgTag src: http://adada
  #    | String This cat
  #    | BTag is -> |
  #                 | ITag funny!
  # In the end we just recursively append

  result = []

  tag_open          = /\[/
  tag_close         = /\]/
  tag_close_prefix  = /\//
  tag_arg           = /=/
  tag_arg_delim     = /&quot;/
  tag_name          = /[-_a-z0-9]/

  current_state = :text
  current_tag = result

  begin
    pos = 0
    while pos < str.length
      case current_state
      when :text
        tmp = ""
        # We iterate through the string either until the end or if we find a [
        while not str[pos] =~ tag_open and pos < str.length
          tmp << str[pos]
          pos = pos.next
        end
        current_tag << tmp
        current_state = :tag_name if pos < str.length # Okay, we have found the beginning of a possible tag!
        pos = pos.next
      when :tag_name
        name = ""
        if str[pos-1] =~ tag_open and str[pos] =~ tag_close_prefix
          # It's a closing tag!
          # Let's check if it applies to our current tag..

          broke_out = false
          tag = current_tag
          until tag.is_a? Array
            len = tag.name.length
            if str[pos+1,len] == tag.name and str[pos+len+1] =~ tag_close
              # If it's the current one?
              if current_tag == tag
                current_tag = tag.parent
                pos += len+1
                broke_out = true
                break
              else
                # It's not the current one! Invalid construct
                raise BBCode::ParseError, "Invalid nested tags"
              end
            end
            tag = tag.parent
          end

          # It's not a closing tag we recognize, so it's text really!
          # Let's restore this!
          if not broke_out
            current_tag << "[/"
          end
          current_state = :text
          pos = pos.next
          next
        end
        while not (str[pos] =~ tag_close and str[pos] =~ tag_arg and pos < str.length) and str[pos] =~ tag_name
          name << str[pos]
          pos = pos.next
        end

        if str[pos] =~ tag_arg or str[pos] =~ tag_close
          if self.get_tag_by_name(name)
            new_tag = self.get_tag_by_name(name).new(current_tag)
            current_tag  << new_tag
            if new_tag.has_option(:content) or new_tag.has_option(:argument)
              current_tag = new_tag
            end
            if str[pos] =~ tag_arg
              if new_tag.has_option :argument
                current_state = :tag_arg
              else
                raise BBCode::ParseError
              end
            else
              current_state = :text
            end
            pos = pos.next
          else
            current_tag << "[#{name}"
            current_state = :text
          end
          next
        end


        # Did we hit the end? Let's get out
        current_tag << name
        current_state = :text
      when :tag_arg
        arg = ""
        while not str[pos] =~ tag_close and pos < str.length
          arg << str[pos]
          pos = pos.next
        end

        current_tag.argument = arg.gsub(/^#{tag_arg_delim}(.*)#{tag_arg_delim}$/, '\1')
        if not current_tag.has_option(:content)
          current_tag = current_tag.parent
        end
        current_state = :text
        pos = pos.next
      else
        pos = pos.next
      end
    end

    result_str = result.map(&:to_s).join('').strip

    # extracted from bb-ruby, which extracted it from Rails ActionPack
    start_tag = '<p>'
    result_str.gsub!(/\r\n?/, "\n")                   # \r\n and \r => \n
    result_str.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline  => paragraph
    #result_str.gsub!(/([^\n>]\n)(?=[^\n<])/, '\1<br>')# 1 newline   => br
    result_str.insert 0, start_tag
    result_str << '</p>'
  rescue BBCode::ParseError => e
    if raise_error
      raise e
    else
      str
    end
  end
end

.tagsObject



8
9
10
# File 'lib/bbcode-rails.rb', line 8

def self.tags
  @tags
end