EliteJournal 1.9.400

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/AUTHORS +1 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +22 -0
  4. data/app/controllers/account_controller.rb +25 -0
  5. data/app/controllers/application.rb +83 -0
  6. data/app/controllers/atom_controller.rb +26 -0
  7. data/app/controllers/auth_controller.rb +23 -0
  8. data/app/controllers/backend_controller.rb +14 -0
  9. data/app/controllers/css_controller.rb +31 -0
  10. data/app/controllers/draft_controller.rb +62 -0
  11. data/app/controllers/image_controller.rb +11 -0
  12. data/app/controllers/journal_controller.rb +53 -0
  13. data/app/controllers/link_controller.rb +29 -0
  14. data/app/controllers/ping_controller.rb +44 -0
  15. data/app/controllers/post_controller.rb +131 -0
  16. data/app/controllers/rss_controller.rb +22 -0
  17. data/app/controllers/tags_controller.rb +74 -0
  18. data/app/controllers/user_controller.rb +30 -0
  19. data/app/controllers/users_controller.rb +13 -0
  20. data/app/helpers/account_helper.rb +2 -0
  21. data/app/helpers/application_helper.rb +117 -0
  22. data/app/helpers/atom_helper.rb +2 -0
  23. data/app/helpers/auth_helper.rb +2 -0
  24. data/app/helpers/backend_helper.rb +2 -0
  25. data/app/helpers/css_helper.rb +2 -0
  26. data/app/helpers/draft_helper.rb +2 -0
  27. data/app/helpers/image_helper.rb +2 -0
  28. data/app/helpers/journal_helper.rb +2 -0
  29. data/app/helpers/link_helper.rb +2 -0
  30. data/app/helpers/ping_helper.rb +5 -0
  31. data/app/helpers/post_helper.rb +2 -0
  32. data/app/helpers/rss_helper.rb +5 -0
  33. data/app/helpers/tags_helper.rb +7 -0
  34. data/app/helpers/user_helper.rb +2 -0
  35. data/app/helpers/users_helper.rb +2 -0
  36. data/app/models/blogger_api.rb +24 -0
  37. data/app/models/comment.rb +18 -0
  38. data/app/models/draft.rb +12 -0
  39. data/app/models/face.rb +24 -0
  40. data/app/models/feed_killer.rb +17 -0
  41. data/app/models/image.rb +7 -0
  42. data/app/models/link.rb +10 -0
  43. data/app/models/meta_weblog_api.rb +82 -0
  44. data/app/models/ping.rb +16 -0
  45. data/app/models/post.rb +30 -0
  46. data/app/models/stylesheet.rb +7 -0
  47. data/app/models/tag.rb +16 -0
  48. data/app/models/user.rb +35 -0
  49. data/app/views/account/info.rhtml +30 -0
  50. data/app/views/atom/feed.rxml +15 -0
  51. data/app/views/auth/login.rhtml +9 -0
  52. data/app/views/css/edit.rhtml +18 -0
  53. data/app/views/css/list.rhtml +24 -0
  54. data/app/views/css/new.rhtml +6 -0
  55. data/app/views/draft/edit.rhtml +9 -0
  56. data/app/views/draft/list.rhtml +22 -0
  57. data/app/views/draft/new.rhtml +9 -0
  58. data/app/views/journal/_comment.rhtml +10 -0
  59. data/app/views/journal/_post.rhtml +40 -0
  60. data/app/views/journal/_trackback.rhtml +4 -0
  61. data/app/views/journal/error.rhtml +1 -0
  62. data/app/views/journal/index.rhtml +1 -0
  63. data/app/views/journal/view.rhtml +15 -0
  64. data/app/views/layouts/application.rhtml +90 -0
  65. data/app/views/link/list.rhtml +19 -0
  66. data/app/views/link/new.rhtml +7 -0
  67. data/app/views/ping/trackback.rxml +4 -0
  68. data/app/views/post/_reply.rhtml +10 -0
  69. data/app/views/post/destroyxml.rxml +3 -0
  70. data/app/views/post/edit.rhtml +7 -0
  71. data/app/views/post/new.rhtml +13 -0
  72. data/app/views/post/postxml.rxml +7 -0
  73. data/app/views/post/reply.rhtml +33 -0
  74. data/app/views/post/replyxml.rxml +5 -0
  75. data/app/views/post/toggle_commentingxml.rxml +9 -0
  76. data/app/views/rss/index.rxml +15 -0
  77. data/app/views/tags/addxml.rxml +4 -0
  78. data/app/views/tags/index.rhtml +3 -0
  79. data/app/views/tags/no_completions.rhtml +1 -0
  80. data/app/views/tags/search.rhtml +10 -0
  81. data/app/views/tags/search_completer.rhtml +1 -0
  82. data/app/views/user/list.rhtml +21 -0
  83. data/app/views/user/new.rhtml +12 -0
  84. data/app/views/users/index.rhtml +6 -0
  85. data/config/app.yml +15 -0
  86. data/config/database.yml +17 -0
  87. data/config/environment.rb +58 -0
  88. data/config/environments/development.rb +5 -0
  89. data/config/environments/geminstall.rb +4 -0
  90. data/config/environments/production.rb +3 -0
  91. data/config/environments/shared.rb +17 -0
  92. data/config/environments/test.rb +3 -0
  93. data/db/db-mysql.sql +95 -0
  94. data/db/db-postgresql.sql +94 -0
  95. data/db/db-sqlite.sql +94 -0
  96. data/db/default_user.sql +1 -0
  97. data/db/development_structure.sql +257 -0
  98. data/elitejournal +132 -0
  99. data/lib/image_size.rb +277 -0
  100. data/lib/trackback.rb +45 -0
  101. data/public/404.html +6 -0
  102. data/public/500.html +6 -0
  103. data/public/dispatch.cgi +10 -0
  104. data/public/dispatch.fcgi +7 -0
  105. data/public/dispatch.rb +10 -0
  106. data/public/stylesheets/ej-layout.css +126 -0
  107. data/public/stylesheets/ej-style.css +105 -0
  108. data/public/stylesheets/undohtml.css +9 -0
  109. metadata +178 -0
data/elitejournal ADDED
@@ -0,0 +1,132 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'webrick'
4
+ require 'optparse'
5
+
6
+ fork_available = true
7
+ begin
8
+ exit unless fork
9
+ rescue NotImplementedError
10
+ fork_available = false
11
+ end
12
+
13
+ OPTIONS = {
14
+ :port => 3000,
15
+ :ip => "127.0.0.1",
16
+ :environment => "development",
17
+ :server_root => File.expand_path(File.dirname(__FILE__) + "/public/"),
18
+ :server_type => fork_available ? WEBrick::Daemon : WEBrick::SimpleServer,
19
+ }
20
+
21
+ ARGV.options do |opts|
22
+ script_name = File.basename($0)
23
+ opts.banner = "Usage: ruby #{script_name} [options]"
24
+
25
+ opts.separator ""
26
+
27
+ opts.on("-p", "--port=port", Integer,
28
+ "Runs Rails on the specified port.",
29
+ "Default: 3000") { |OPTIONS[:port]| }
30
+ opts.on("-b", "--binding=ip", String,
31
+ "Binds Rails to the specified ip.",
32
+ "Default: 127.0.0.1") { |OPTIONS[:ip]| }
33
+ opts.on("-d", "--daemon",
34
+ "Make Rails run as a Daemon (only works if fork is available -- meaning on *nix)."
35
+ ) { OPTIONS[:server_type] = WEBrick::Daemon }
36
+
37
+ opts.separator ""
38
+
39
+ opts.on("-h", "--help",
40
+ "Show this help message.") { puts opts; exit }
41
+
42
+ opts.parse!
43
+ end
44
+
45
+ # Hack it up for the gem
46
+ unless File.exist? "#{ENV['HOME']}/.elitejournal"
47
+ require 'rubygems'
48
+ require 'sqlite'
49
+ puts 'Running Elite Journal for the first time ...'
50
+
51
+ database_yml =<<DBYML
52
+ development:
53
+ adapter: sqlite
54
+ dbfile: #{ENV['HOME']}/.elitejournal/elitejournal.db
55
+
56
+ production:
57
+ adapter: sqlite
58
+ dbfile: #{ENV['HOME']}/.elitejournal/elitejournal.db
59
+
60
+ test:
61
+ adapter: sqlite
62
+ dbfile: #{ENV['HOME']}/.elitejournal/elitejournal.db
63
+
64
+ geminstall:
65
+ adapter: sqlite
66
+ dbfile: #{ENV['HOME']}/.elitejournal/elitejournal.db
67
+ DBYML
68
+
69
+ puts 'Creating environment ...'
70
+ Dir.mkdir "#{ENV['HOME']}/.elitejournal"
71
+ File.open("#{ENV['HOME']}/.elitejournal/database.yml", 'w') do |f|
72
+ f.write(database_yml)
73
+ end
74
+
75
+ db = SQLite::Database.new("#{ENV['HOME']}/.elitejournal/elitejournal.db", 0)
76
+ puts "Creating sqlite database ..."
77
+ dbdef = File.open(File.dirname(__FILE__) + "/db/db-sqlite.sql", "r").readlines.join()
78
+ defusr = File.open(File.dirname(__FILE__) + "/db/default_user.sql", "r").readlines.join()
79
+ db.execute_batch dbdef
80
+ db.execute_batch defusr
81
+ db.close
82
+ puts 'Done.'
83
+ end
84
+
85
+
86
+
87
+ ENV["RAILS_ENV"] = 'geminstall'
88
+
89
+ require File.dirname(__FILE__) + "/config/environment"
90
+ require 'webrick_server'
91
+
92
+ # This is so digusting I can hardly look at it. But it works. Long live Ruby!
93
+ class DispatchServlet
94
+ alias :orig_do_GET :do_GET
95
+
96
+ def do_GET(req, res)
97
+ if /\/(\d{4})\/(\d{1,2})\/(\d{1,2})\/?/ =~ req.request_uri.path
98
+ req.request_uri.path = "/dispatch.rb"
99
+ req.request_uri.query = "controller=journal&action=range&year=#{$1}&month=#{$2}&day=#{$3}"
100
+ handle_dispatch(req, res)
101
+ return
102
+ end
103
+ if /\/(\d{4})\/(\d{1,2})\/?/ =~ req.request_uri.path
104
+ req.request_uri.path = "/dispatch.rb"
105
+ req.request_uri.query = "controller=journal&action=range&year=#{$1}&month=#{$2}"
106
+ handle_dispatch(req, res)
107
+ return
108
+ end
109
+ if /\/(\d{4})\/?/ =~ req.request_uri.path
110
+ req.request_uri.path = "/dispatch.rb"
111
+ req.request_uri.query = "controller=journal&action=range&year=#{$1}"
112
+ handle_dispatch(req, res)
113
+ return
114
+ end
115
+ if /\/view\/(\d+)\/?/ =~ req.request_uri.path
116
+ req.request_uri.path = "/dispatch.rb"
117
+ req.request_uri.query = "controller=journal&action=view&id=#{$1}"
118
+ handle_dispatch(req, res)
119
+ return
120
+ end
121
+
122
+ orig_do_GET(req, res)
123
+ end
124
+ end
125
+
126
+
127
+
128
+
129
+ OPTIONS[:index_controller] = "journal"
130
+
131
+ puts "=> Elite Journal started on http://#{OPTIONS[:ip]}:#{OPTIONS[:port]}"
132
+ DispatchServlet.dispatch(OPTIONS)
data/lib/image_size.rb ADDED
@@ -0,0 +1,277 @@
1
+ #!ruby
2
+
3
+ class ImageSize
4
+ # Image Type Constants
5
+ module Type
6
+ OTHER = "OTHER"
7
+ GIF = "GIF"
8
+ PNG = "PNG"
9
+ JPEG = "JPEG"
10
+ BMP = "BMP"
11
+ PPM = "PPM" # PPM is like PBM, PGM, & XV
12
+ PBM = "PBM"
13
+ PGM = "PGM"
14
+ # XV = "XV"
15
+ XBM = "XBM"
16
+ TIFF = "TIFF"
17
+ XPM = "XPM"
18
+ PSD = "PSD"
19
+ PCX = "PCX"
20
+ SWF = "SWF"
21
+ end
22
+
23
+ JpegCodeCheck = [
24
+ "\xc0", "\xc1", "\xc2", "\xc3",
25
+ "\xc5", "\xc6", "\xc7",
26
+ "\xc9", "\xca", "\xcb",
27
+ "\xcd", "\xce", "\xcf",
28
+ ]
29
+
30
+ # image type list
31
+ def ImageSize.type_list
32
+ Type.constants
33
+ end
34
+
35
+ # receive image & make size
36
+ # argument is image String or IO
37
+ def initialize(img_data, img_type = nil)
38
+ @img_data = img_data.dup
39
+ @img_width = nil
40
+ @img_height = nil
41
+
42
+ if @img_data.is_a?(IO)
43
+ @img_top = @img_data.read(128)
44
+ @img_data.seek(0, 0)
45
+ # define Singleton-method definition to IO (byte, offset)
46
+ def @img_data.read_o(length = 1, offset = nil)
47
+ self.seek(offset, 0) if offset
48
+ ret = self.read(length)
49
+ raise "cannot read!!" unless ret
50
+ ret
51
+ end
52
+ elsif @img_data.is_a?(String)
53
+ @img_top = @img_data[0, 128]
54
+ # define Singleton-method definition to String (byte, offset)
55
+ def @img_data.read_o(length = 1, offset = nil)
56
+ @img_offset = 0 if !(defined?(@img_offset))
57
+ @img_offset = offset if offset
58
+ ret = self[@img_offset, length]
59
+ @img_offset += length
60
+ ret
61
+ end
62
+ else
63
+ raise "argument class error!! #{img_data.type}"
64
+ end
65
+
66
+ if img_type.nil?
67
+ @img_type = check_type()
68
+ else
69
+ match = false
70
+ Type.constants.each do |t|
71
+ match = true if img_type == t
72
+ end
73
+ raise("img_type is failed. #{img_type}\n") if match == false
74
+ @img_type = img_type
75
+ end
76
+
77
+ eval("@img_width, @img_height = measure_" + @img_type + "()") if @img_type != Type::OTHER
78
+ end
79
+
80
+ # get parameter
81
+ def get_type; @img_type; end
82
+ def get_height
83
+ if @img_type == Type::OTHER then nil else @img_height end
84
+ end
85
+ def get_width
86
+ if @img_type == Type::OTHER then nil else @img_width end
87
+ end
88
+
89
+ def check_type()
90
+ if @img_top =~ /^GIF8[7,9]a/ then Type::GIF
91
+ elsif @img_top[0, 8] == "\x89PNG\x0d\x0a\x1a\x0a" then Type::PNG
92
+ elsif @img_top[0, 2] == "\xFF\xD8" then Type::JPEG
93
+ elsif @img_top[0, 2] == 'BM' then Type::BMP
94
+ elsif @img_top =~ /^P[1-7]/ then Type::PPM
95
+ elsif @img_top =~ /\#define\s+\S+\s+\d+/ then Type::XBM
96
+ elsif @img_top[0, 4] == "MM\x00\x2a" then Type::TIFF
97
+ elsif @img_top[0, 4] == "II\x2a\x00" then Type::TIFF
98
+ elsif @img_top =~ /\/\* XPM \*\// then Type::XPM
99
+ elsif @img_top[0, 4] == "8BPS" then Type::PSD
100
+ elsif @img_top[0, 3] == "FWS" then Type::SWF
101
+ elsif @img_top[0] == 10 then Type::PCX
102
+ else Type::OTHER
103
+ end
104
+ end
105
+ private(:check_type)
106
+
107
+ def measure_GIF()
108
+ @img_data.read_o(6)
109
+ @img_data.read_o(4).unpack('vv')
110
+ end
111
+ private(:measure_GIF)
112
+
113
+ def measure_PNG()
114
+ @img_data.read_o(12)
115
+ raise "This file is not PNG." unless @img_data.read_o(4) == "IHDR"
116
+ @img_data.read_o(8).unpack('NN')
117
+ end
118
+ private(:measure_PNG)
119
+
120
+ def measure_JPEG()
121
+ c_marker = "\xFF" # Section marker.
122
+ @img_data.read_o(2)
123
+ while(true)
124
+ marker, code, length = @img_data.read_o(4).unpack('aan')
125
+ raise "JPEG marker not found!" if marker != c_marker
126
+
127
+ if JpegCodeCheck.include?(code)
128
+ height, width = @img_data.read_o(5).unpack('xnn')
129
+ return([width, height])
130
+ end
131
+ @img_data.read_o(length - 2)
132
+ end
133
+ end
134
+ private(:measure_JPEG)
135
+
136
+ def measure_BMP()
137
+ @img_data.read_o(26).unpack("x18VV");
138
+ end
139
+ private(:measure_BMP)
140
+
141
+ def measure_PPM()
142
+ header = @img_data.read_o(1024)
143
+ header.gsub!(/^\#[^\n\r]*/m, "")
144
+ header =~ /^(P[1-6])\s+?(\d+)\s+?(\d+)/m
145
+ width = $2.to_i; height = $3.to_i
146
+ case $1
147
+ when "P1", "P4" then @img_type = "PBM"
148
+ when "P2", "P5" then @img_type = "PGM"
149
+ when "P3", "P6" then @img_type = "PPM"
150
+ # when "P7"
151
+ # @img_type = "XV"
152
+ # header =~ /IMGINFO:(\d+)x(\d+)/m
153
+ # width = $1.to_i; height = $2.to_i
154
+ end
155
+ [width, height]
156
+ end
157
+ private(:measure_PPM)
158
+
159
+ alias :measure_PGM :measure_PPM
160
+ private(:measure_PGM)
161
+ alias :measure_PBM :measure_PPM
162
+ private(:measure_PBM)
163
+
164
+ def measure_XBM()
165
+ @img_data.read_o(1024) =~ /^\#define\s*\S*\s*(\d+)\s*\n\#define\s*\S*\s*(\d+)/mi
166
+ [$1.to_i, $2.to_i]
167
+ end
168
+ private(:measure_XBM)
169
+
170
+ def measure_XPM()
171
+ width = height = nil
172
+ while(line = @img_data.read_o(1024))
173
+ if line =~ /"\s*(\d+)\s+(\d+)(\s+\d+\s+\d+){1,2}\s*"/m
174
+ width = $1.to_i; height = $2.to_i
175
+ break
176
+ end
177
+ end
178
+ [width, height]
179
+ end
180
+ private(:measure_XPM)
181
+
182
+ def measure_PSD()
183
+ @img_data.read_o(26).unpack("x14NN")
184
+ end
185
+ private(:measure_PSD)
186
+
187
+ def measure_TIFF()
188
+ endian = if (@img_data.read_o(4) =~ /II\x2a\x00/o) then 'v' else 'n' end
189
+ # 'v' little-endian 'n' default to big-endian
190
+
191
+ packspec = [
192
+ nil, # nothing (shouldn't happen)
193
+ 'C', # BYTE (8-bit unsigned integer)
194
+ nil, # ASCII
195
+ endian, # SHORT (16-bit unsigned integer)
196
+ endian.upcase, # LONG (32-bit unsigned integer)
197
+ nil, # RATIONAL
198
+ 'c', # SBYTE (8-bit signed integer)
199
+ nil, # UNDEFINED
200
+ endian, # SSHORT (16-bit unsigned integer)
201
+ endian.upcase, # SLONG (32-bit unsigned integer)
202
+ ]
203
+
204
+ offset = @img_data.read_o(4).unpack(endian.upcase)[0] # Get offset to IFD
205
+
206
+ ifd = @img_data.read_o(2, offset)
207
+ num_dirent = ifd.unpack(endian)[0] # Make it useful
208
+ offset += 2
209
+ num_dirent = offset + (num_dirent * 12); # Calc. maximum offset of IFD
210
+
211
+ ifd = width = height = nil
212
+ while(width.nil? || height.nil?)
213
+ ifd = @img_data.read_o(12, offset) # Get first directory entry
214
+ break if (ifd.nil? || (offset > num_dirent))
215
+ offset += 12
216
+ tag = ifd.unpack(endian)[0] # ...and decode its tag
217
+ type = ifd[2, 2].unpack(endian)[0] # ...and the data type
218
+
219
+ # Check the type for sanity.
220
+ next if (type > packspec.size + 0) || (packspec[type].nil?)
221
+ if tag == 0x0100 # Decode the value
222
+ width = ifd[8, 4].unpack(packspec[type])[0]
223
+ elsif tag == 0x0101 # Decode the value
224
+ height = ifd[8, 4].unpack(packspec[type])[0]
225
+ end
226
+ end
227
+
228
+ raise "#{if width.nil? then 'width not defined.' end} #{if height.nil? then 'height not defined.' end}" if width.nil? || height.nil?
229
+ [width, height]
230
+ end
231
+ private(:measure_TIFF)
232
+
233
+ def measure_PCX()
234
+ header = @img_data.read_o(128)
235
+ head_part = header.unpack('C4S4')
236
+ width = head_part[6] - head_part[4] + 1
237
+ height = head_part[7] - head_part[5] + 1
238
+ [width, height]
239
+ end
240
+ private(:measure_PCX)
241
+
242
+ def measure_SWF()
243
+ header = @img_data.read_o(9)
244
+ raise("This file is not SWF.") unless header.unpack('a3')[0] == 'FWS'
245
+
246
+ bit_length = Integer("0b#{header.unpack('@8B5')}")
247
+ header << @img_data.read_o(bit_length*4/8+1)
248
+ str = header.unpack("@8B#{5+bit_length*4}")[0]
249
+ last = 5
250
+ x_min = Integer("0b#{str[last,bit_length]}")
251
+ x_max = Integer("0b#{str[(last += bit_length),bit_length]}")
252
+ y_min = Integer("0b#{str[(last += bit_length),bit_length]}")
253
+ y_max = Integer("0b#{str[(last += bit_length),bit_length]}")
254
+ width = (x_max - x_min)/20
255
+ height = (y_max - y_min)/20
256
+ [width, height]
257
+ end
258
+ private(:measure_PCX)
259
+ end
260
+
261
+
262
+ if __FILE__ == $0
263
+ print "TypeList: #{ImageSize.type.inspect}\n"
264
+
265
+ Dir.glob("*").each do |file|
266
+ print "#{file} (string)\n"
267
+ open(file, "rb") do |fh|
268
+ img = ImageSize.new(fh.read)
269
+ print <<-EOF
270
+ type: #{img.get_type.inspect}
271
+ width: #{img.get_width.inspect}
272
+ height: #{img.get_height.inspect}
273
+ EOF
274
+ end
275
+ end
276
+ end
277
+
data/lib/trackback.rb ADDED
@@ -0,0 +1,45 @@
1
+ def send_trackback(post, url, app_config)
2
+ require 'uri'
3
+ require 'net/http'
4
+ begin
5
+ uri = URI.parse(url)
6
+ oururi = URI.parse(url_for(:controller => 'ping', :action => 'trackback', :only_path => false))
7
+ if oururi.host == uri.host && oururi.port == uri.port
8
+ # This is the same EJ instance, which may not be able to handle
9
+ # simultaneous connections (webrick), so dump this ping right to the DB
10
+ if /.*\/(\d{1,7})/ =~ url
11
+ origpost = Post.find($1) rescue nil
12
+ return if origpost.nil?
13
+ ping = origpost.build_to_pings
14
+ ping.title = post.subject
15
+ logger.info "LOGGING #{strip_html(post.rendered, false)[0..255]}"
16
+ ping.excerpt = strip_html(post.rendered, false)[0..255]
17
+ ping.url = url_for(:controller => 'journal', :action => 'view', :id => post.id, :only_path => false)
18
+ ping.blog_name = post.user.title
19
+ ping.save
20
+ end
21
+ return
22
+ end
23
+ qrystr = "title=#{URI.escape(post.subject)}"
24
+ qrystr << "&excerpt=#{strip_html(post.rendered)[0..255]}"
25
+ qrystr << "&url=#{@request.protocol}#{@request.host_with_port}/journal/view/#{post.id}"
26
+ qrystr << "&blog_name=#{URI.escape(post.user.title)}"
27
+
28
+ Net::HTTP.start(uri.host, uri.port) do |http|
29
+ http.post("#{uri.path}?#{uri.query}", qrystr)
30
+ # I don't really care if this fails or not right now.
31
+ end
32
+ rescue
33
+ end
34
+ end
35
+
36
+ def strip_html(text, escape=true)
37
+ attribute_key = /[\w:_-]+/
38
+ attribute_value = /(?:[A-Za-z0-9]+|(?:'[^']*?'|"[^"]*?"))/
39
+ attribute = /(?:#{attribute_key}(?:\s*=\s*#{attribute_value})?)/
40
+ attributes = /(?:#{attribute}(?:\s+#{attribute})*)/
41
+ tag_key = attribute_key
42
+ tag = %r{<[!/?\[]?(?:#{tag_key}|--)(?:\s+#{attributes})?\s*(?:[!/?\]]+|--)?>}
43
+ text.gsub(tag, '').gsub(/\s+/, ' ').strip
44
+ escape ? CGI::escape(text) : text
45
+ end