EliteJournal 1.9.400

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.
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