~ubuntu-branches/ubuntu/hardy/ruby1.8/hardy-updates

« back to all changes in this revision

Viewing changes to lib/cgi.rb

  • Committer: Bazaar Package Importer
  • Author(s): akira yamada
  • Date: 2007-03-13 22:11:58 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20070313221158-h3oql37brlaf2go2
Tags: 1.8.6-1
* new upstream version, 1.8.6.
* libruby1.8 conflicts with libopenssl-ruby1.8 (< 1.8.6) (closes: #410018)
* changed packaging style to cdbs from dbs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
2
# cgi.rb - cgi support library
 
3
 
4
# Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
 
5
 
6
# Copyright (C) 2000  Information-technology Promotion Agency, Japan
 
7
#
 
8
# Author: Wakou Aoyama <wakou@ruby-lang.org>
 
9
#
 
10
# Documentation: Wakou Aoyama (RDoc'd and embellished by William Webber) 
 
11
 
12
# == Overview
 
13
#
 
14
# The Common Gateway Interface (CGI) is a simple protocol
 
15
# for passing an HTTP request from a web server to a
 
16
# standalone program, and returning the output to the web
 
17
# browser.  Basically, a CGI program is called with the
 
18
# parameters of the request passed in either in the
 
19
# environment (GET) or via $stdin (POST), and everything
 
20
# it prints to $stdout is returned to the client.
 
21
 
22
# This file holds the +CGI+ class.  This class provides
 
23
# functionality for retrieving HTTP request parameters,
 
24
# managing cookies, and generating HTML output.  See the
 
25
# class documentation for more details and examples of use.
 
26
#
 
27
# The file cgi/session.rb provides session management
 
28
# functionality; see that file for more details.
 
29
#
 
30
# See http://www.w3.org/CGI/ for more information on the CGI
 
31
# protocol.
 
32
 
 
33
raise "Please, use ruby 1.5.4 or later." if RUBY_VERSION < "1.5.4"
 
34
 
 
35
require 'English'
 
36
 
 
37
# CGI class.  See documentation for the file cgi.rb for an overview
 
38
# of the CGI protocol.
 
39
#
 
40
# == Introduction
 
41
#
 
42
# CGI is a large class, providing several categories of methods, many of which
 
43
# are mixed in from other modules.  Some of the documentation is in this class,
 
44
# some in the modules CGI::QueryExtension and CGI::HtmlExtension.  See
 
45
# CGI::Cookie for specific information on handling cookies, and cgi/session.rb
 
46
# (CGI::Session) for information on sessions.
 
47
#
 
48
# For queries, CGI provides methods to get at environmental variables,
 
49
# parameters, cookies, and multipart request data.  For responses, CGI provides
 
50
# methods for writing output and generating HTML.
 
51
#
 
52
# Read on for more details.  Examples are provided at the bottom.
 
53
#
 
54
# == Queries
 
55
#
 
56
# The CGI class dynamically mixes in parameter and cookie-parsing
 
57
# functionality,  environmental variable access, and support for
 
58
# parsing multipart requests (including uploaded files) from the
 
59
# CGI::QueryExtension module.
 
60
#
 
61
# === Environmental Variables
 
62
#
 
63
# The standard CGI environmental variables are available as read-only
 
64
# attributes of a CGI object.  The following is a list of these variables:
 
65
#
 
66
#
 
67
#   AUTH_TYPE               HTTP_HOST          REMOTE_IDENT
 
68
#   CONTENT_LENGTH          HTTP_NEGOTIATE     REMOTE_USER
 
69
#   CONTENT_TYPE            HTTP_PRAGMA        REQUEST_METHOD
 
70
#   GATEWAY_INTERFACE       HTTP_REFERER       SCRIPT_NAME
 
71
#   HTTP_ACCEPT             HTTP_USER_AGENT    SERVER_NAME
 
72
#   HTTP_ACCEPT_CHARSET     PATH_INFO          SERVER_PORT
 
73
#   HTTP_ACCEPT_ENCODING    PATH_TRANSLATED    SERVER_PROTOCOL
 
74
#   HTTP_ACCEPT_LANGUAGE    QUERY_STRING       SERVER_SOFTWARE
 
75
#   HTTP_CACHE_CONTROL      REMOTE_ADDR
 
76
#   HTTP_FROM               REMOTE_HOST
 
77
#
 
78
#
 
79
# For each of these variables, there is a corresponding attribute with the
 
80
# same name, except all lower case and without a preceding HTTP_.  
 
81
# +content_length+ and +server_port+ are integers; the rest are strings.
 
82
#
 
83
# === Parameters
 
84
#
 
85
# The method #params() returns a hash of all parameters in the request as
 
86
# name/value-list pairs, where the value-list is an Array of one or more
 
87
# values.  The CGI object itself also behaves as a hash of parameter names 
 
88
# to values, but only returns a single value (as a String) for each 
 
89
# parameter name.
 
90
#
 
91
# For instance, suppose the request contains the parameter 
 
92
# "favourite_colours" with the multiple values "blue" and "green".  The
 
93
# following behaviour would occur:
 
94
#
 
95
#   cgi.params["favourite_colours"]  # => ["blue", "green"]
 
96
#   cgi["favourite_colours"]         # => "blue"
 
97
#
 
98
# If a parameter does not exist, the former method will return an empty
 
99
# array, the latter an empty string.  The simplest way to test for existence
 
100
# of a parameter is by the #has_key? method.
 
101
#
 
102
# === Cookies
 
103
#
 
104
# HTTP Cookies are automatically parsed from the request.  They are available
 
105
# from the #cookies() accessor, which returns a hash from cookie name to
 
106
# CGI::Cookie object.
 
107
#
 
108
# === Multipart requests
 
109
#
 
110
# If a request's method is POST and its content type is multipart/form-data, 
 
111
# then it may contain uploaded files.  These are stored by the QueryExtension
 
112
# module in the parameters of the request.  The parameter name is the name
 
113
# attribute of the file input field, as usual.  However, the value is not
 
114
# a string, but an IO object, either an IOString for small files, or a
 
115
# Tempfile for larger ones.  This object also has the additional singleton
 
116
# methods:
 
117
#
 
118
# #local_path():: the path of the uploaded file on the local filesystem
 
119
# #original_filename():: the name of the file on the client computer
 
120
# #content_type():: the content type of the file
 
121
#
 
122
# == Responses
 
123
#
 
124
# The CGI class provides methods for sending header and content output to
 
125
# the HTTP client, and mixes in methods for programmatic HTML generation
 
126
# from CGI::HtmlExtension and CGI::TagMaker modules.  The precise version of HTML
 
127
# to use for HTML generation is specified at object creation time.
 
128
#
 
129
# === Writing output
 
130
#
 
131
# The simplest way to send output to the HTTP client is using the #out() method.
 
132
# This takes the HTTP headers as a hash parameter, and the body content
 
133
# via a block.  The headers can be generated as a string using the #header()
 
134
# method.  The output stream can be written directly to using the #print()
 
135
# method.
 
136
#
 
137
# === Generating HTML
 
138
#
 
139
# Each HTML element has a corresponding method for generating that
 
140
# element as a String.  The name of this method is the same as that
 
141
# of the element, all lowercase.  The attributes of the element are 
 
142
# passed in as a hash, and the body as a no-argument block that evaluates
 
143
# to a String.  The HTML generation module knows which elements are
 
144
# always empty, and silently drops any passed-in body.  It also knows
 
145
# which elements require matching closing tags and which don't.  However,
 
146
# it does not know what attributes are legal for which elements.
 
147
#
 
148
# There are also some additional HTML generation methods mixed in from
 
149
# the CGI::HtmlExtension module.  These include individual methods for the
 
150
# different types of form inputs, and methods for elements that commonly
 
151
# take particular attributes where the attributes can be directly specified
 
152
# as arguments, rather than via a hash.
 
153
#
 
154
# == Examples of use
 
155
 
156
# === Get form values
 
157
 
158
#   require "cgi"
 
159
#   cgi = CGI.new
 
160
#   value = cgi['field_name']   # <== value string for 'field_name'
 
161
#     # if not 'field_name' included, then return "".
 
162
#   fields = cgi.keys            # <== array of field names
 
163
 
164
#   # returns true if form has 'field_name'
 
165
#   cgi.has_key?('field_name')
 
166
#   cgi.has_key?('field_name')
 
167
#   cgi.include?('field_name')
 
168
 
169
# CAUTION! cgi['field_name'] returned an Array with the old 
 
170
# cgi.rb(included in ruby 1.6)
 
171
 
172
# === Get form values as hash
 
173
 
174
#   require "cgi"
 
175
#   cgi = CGI.new
 
176
#   params = cgi.params
 
177
 
178
# cgi.params is a hash.
 
179
 
180
#   cgi.params['new_field_name'] = ["value"]  # add new param
 
181
#   cgi.params['field_name'] = ["new_value"]  # change value
 
182
#   cgi.params.delete('field_name')           # delete param
 
183
#   cgi.params.clear                          # delete all params
 
184
 
185
 
186
# === Save form values to file
 
187
 
188
#   require "pstore"
 
189
#   db = PStore.new("query.db")
 
190
#   db.transaction do
 
191
#     db["params"] = cgi.params
 
192
#   end
 
193
 
194
 
195
# === Restore form values from file
 
196
 
197
#   require "pstore"
 
198
#   db = PStore.new("query.db")
 
199
#   db.transaction do
 
200
#     cgi.params = db["params"]
 
201
#   end
 
202
 
203
 
204
# === Get multipart form values
 
205
 
206
#   require "cgi"
 
207
#   cgi = CGI.new
 
208
#   value = cgi['field_name']   # <== value string for 'field_name'
 
209
#   value.read                  # <== body of value
 
210
#   value.local_path            # <== path to local file of value
 
211
#   value.original_filename     # <== original filename of value
 
212
#   value.content_type          # <== content_type of value
 
213
 
214
# and value has StringIO or Tempfile class methods.
 
215
 
216
# === Get cookie values
 
217
 
218
#   require "cgi"
 
219
#   cgi = CGI.new
 
220
#   values = cgi.cookies['name']  # <== array of 'name'
 
221
#     # if not 'name' included, then return [].
 
222
#   names = cgi.cookies.keys      # <== array of cookie names
 
223
 
224
# and cgi.cookies is a hash.
 
225
 
226
# === Get cookie objects
 
227
 
228
#   require "cgi"
 
229
#   cgi = CGI.new
 
230
#   for name, cookie in cgi.cookies
 
231
#     cookie.expires = Time.now + 30
 
232
#   end
 
233
#   cgi.out("cookie" => cgi.cookies) {"string"}
 
234
 
235
#   cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... }
 
236
 
237
#   require "cgi"
 
238
#   cgi = CGI.new
 
239
#   cgi.cookies['name'].expires = Time.now + 30
 
240
#   cgi.out("cookie" => cgi.cookies['name']) {"string"}
 
241
 
242
# === Print http header and html string to $DEFAULT_OUTPUT ($>)
 
243
 
244
#   require "cgi"
 
245
#   cgi = CGI.new("html3")  # add HTML generation methods
 
246
#   cgi.out() do
 
247
#     cgi.html() do
 
248
#       cgi.head{ cgi.title{"TITLE"} } +
 
249
#       cgi.body() do
 
250
#         cgi.form() do
 
251
#           cgi.textarea("get_text") +
 
252
#           cgi.br +
 
253
#           cgi.submit
 
254
#         end +
 
255
#         cgi.pre() do
 
256
#           CGI::escapeHTML(
 
257
#             "params: " + cgi.params.inspect + "\n" +
 
258
#             "cookies: " + cgi.cookies.inspect + "\n" +
 
259
#             ENV.collect() do |key, value|
 
260
#               key + " --> " + value + "\n"
 
261
#             end.join("")
 
262
#           )
 
263
#         end
 
264
#       end
 
265
#     end
 
266
#   end
 
267
 
268
#   # add HTML generation methods
 
269
#   CGI.new("html3")    # html3.2
 
270
#   CGI.new("html4")    # html4.01 (Strict)
 
271
#   CGI.new("html4Tr")  # html4.01 Transitional
 
272
#   CGI.new("html4Fr")  # html4.01 Frameset
 
273
#
 
274
class CGI
 
275
 
 
276
  # :stopdoc:
 
277
 
 
278
  # String for carriage return
 
279
  CR  = "\015"
 
280
 
 
281
  # String for linefeed
 
282
  LF  = "\012"
 
283
 
 
284
  # Standard internet newline sequence
 
285
  EOL = CR + LF
 
286
 
 
287
  REVISION = '$Id: cgi.rb 12050 2007-03-12 17:55:03Z knu $' #:nodoc:
 
288
 
 
289
  NEEDS_BINMODE = true if /WIN/ni.match(RUBY_PLATFORM) 
 
290
 
 
291
  # Path separators in different environments.
 
292
  PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'}
 
293
 
 
294
  # HTTP status codes.
 
295
  HTTP_STATUS = {
 
296
    "OK"                  => "200 OK",
 
297
    "PARTIAL_CONTENT"     => "206 Partial Content",
 
298
    "MULTIPLE_CHOICES"    => "300 Multiple Choices",
 
299
    "MOVED"               => "301 Moved Permanently",
 
300
    "REDIRECT"            => "302 Found",
 
301
    "NOT_MODIFIED"        => "304 Not Modified",
 
302
    "BAD_REQUEST"         => "400 Bad Request",
 
303
    "AUTH_REQUIRED"       => "401 Authorization Required",
 
304
    "FORBIDDEN"           => "403 Forbidden",
 
305
    "NOT_FOUND"           => "404 Not Found",
 
306
    "METHOD_NOT_ALLOWED"  => "405 Method Not Allowed",
 
307
    "NOT_ACCEPTABLE"      => "406 Not Acceptable",
 
308
    "LENGTH_REQUIRED"     => "411 Length Required",
 
309
    "PRECONDITION_FAILED" => "412 Rrecondition Failed",
 
310
    "SERVER_ERROR"        => "500 Internal Server Error",
 
311
    "NOT_IMPLEMENTED"     => "501 Method Not Implemented",
 
312
    "BAD_GATEWAY"         => "502 Bad Gateway",
 
313
    "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates"
 
314
  }
 
315
 
 
316
  # Abbreviated day-of-week names specified by RFC 822
 
317
  RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ]
 
318
 
 
319
  # Abbreviated month names specified by RFC 822
 
320
  RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ]
 
321
 
 
322
  # :startdoc:
 
323
 
 
324
  def env_table 
 
325
    ENV
 
326
  end
 
327
 
 
328
  def stdinput
 
329
    $stdin
 
330
  end
 
331
 
 
332
  def stdoutput
 
333
    $DEFAULT_OUTPUT
 
334
  end
 
335
 
 
336
  private :env_table, :stdinput, :stdoutput
 
337
 
 
338
  # URL-encode a string.
 
339
  #   url_encoded_string = CGI::escape("'Stop!' said Fred")
 
340
  #      # => "%27Stop%21%27+said+Fred"
 
341
  def CGI::escape(string)
 
342
    string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
 
343
      '%' + $1.unpack('H2' * $1.size).join('%').upcase
 
344
    end.tr(' ', '+')
 
345
  end
 
346
 
 
347
 
 
348
  # URL-decode a string.
 
349
  #   string = CGI::unescape("%27Stop%21%27+said+Fred")
 
350
  #      # => "'Stop!' said Fred"
 
351
  def CGI::unescape(string)
 
352
    string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
 
353
      [$1.delete('%')].pack('H*')
 
354
    end
 
355
  end
 
356
 
 
357
 
 
358
  # Escape special characters in HTML, namely &\"<>
 
359
  #   CGI::escapeHTML('Usage: foo "bar" <baz>')
 
360
  #      # => "Usage: foo &quot;bar&quot; &lt;baz&gt;"
 
361
  def CGI::escapeHTML(string)
 
362
    string.gsub(/&/n, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;')
 
363
  end
 
364
 
 
365
 
 
366
  # Unescape a string that has been HTML-escaped
 
367
  #   CGI::unescapeHTML("Usage: foo &quot;bar&quot; &lt;baz&gt;")
 
368
  #      # => "Usage: foo \"bar\" <baz>"
 
369
  def CGI::unescapeHTML(string)
 
370
    string.gsub(/&(amp|quot|gt|lt|\#[0-9]+|\#x[0-9A-Fa-f]+);/n) do
 
371
      match = $1.dup
 
372
      case match
 
373
      when 'amp'                 then '&'
 
374
      when 'quot'                then '"'
 
375
      when 'gt'                  then '>'
 
376
      when 'lt'                  then '<'
 
377
      when /\A#0*(\d+)\z/n       then
 
378
        if Integer($1) < 256
 
379
          Integer($1).chr
 
380
        else
 
381
          if Integer($1) < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U)
 
382
            [Integer($1)].pack("U")
 
383
          else
 
384
            "&##{$1};"
 
385
          end
 
386
        end
 
387
      when /\A#x([0-9a-f]+)\z/ni then
 
388
        if $1.hex < 256
 
389
          $1.hex.chr
 
390
        else
 
391
          if $1.hex < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U)
 
392
            [$1.hex].pack("U")
 
393
          else
 
394
            "&#x#{$1};"
 
395
          end
 
396
        end
 
397
      else
 
398
        "&#{match};"
 
399
      end
 
400
    end
 
401
  end
 
402
 
 
403
 
 
404
  # Escape only the tags of certain HTML elements in +string+.
 
405
  #
 
406
  # Takes an element or elements or array of elements.  Each element
 
407
  # is specified by the name of the element, without angle brackets.
 
408
  # This matches both the start and the end tag of that element.
 
409
  # The attribute list of the open tag will also be escaped (for
 
410
  # instance, the double-quotes surrounding attribute values).
 
411
  #
 
412
  #   print CGI::escapeElement('<BR><A HREF="url"></A>', "A", "IMG")
 
413
  #     # "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt"
 
414
  #
 
415
  #   print CGI::escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"])
 
416
  #     # "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt"
 
417
  def CGI::escapeElement(string, *elements)
 
418
    elements = elements[0] if elements[0].kind_of?(Array)
 
419
    unless elements.empty?
 
420
      string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do
 
421
        CGI::escapeHTML($&)
 
422
      end
 
423
    else
 
424
      string
 
425
    end
 
426
  end
 
427
 
 
428
 
 
429
  # Undo escaping such as that done by CGI::escapeElement()
 
430
  #
 
431
  #   print CGI::unescapeElement(
 
432
  #           CGI::escapeHTML('<BR><A HREF="url"></A>'), "A", "IMG")
 
433
  #     # "&lt;BR&gt;<A HREF="url"></A>"
 
434
  # 
 
435
  #   print CGI::unescapeElement(
 
436
  #           CGI::escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"])
 
437
  #     # "&lt;BR&gt;<A HREF="url"></A>"
 
438
  def CGI::unescapeElement(string, *elements)
 
439
    elements = elements[0] if elements[0].kind_of?(Array)
 
440
    unless elements.empty?
 
441
      string.gsub(/&lt;\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?&gt;/ni) do
 
442
        CGI::unescapeHTML($&)
 
443
      end
 
444
    else
 
445
      string
 
446
    end
 
447
  end
 
448
 
 
449
 
 
450
  # Format a +Time+ object as a String using the format specified by RFC 1123.
 
451
  #
 
452
  #   CGI::rfc1123_date(Time.now)
 
453
  #     # Sat, 01 Jan 2000 00:00:00 GMT
 
454
  def CGI::rfc1123_date(time)
 
455
    t = time.clone.gmtime
 
456
    return format("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
 
457
                RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year,
 
458
                t.hour, t.min, t.sec)
 
459
  end
 
460
 
 
461
 
 
462
  # Create an HTTP header block as a string.
 
463
  #
 
464
  # Includes the empty line that ends the header block.
 
465
  #
 
466
  # +options+ can be a string specifying the Content-Type (defaults
 
467
  # to text/html), or a hash of header key/value pairs.  The following
 
468
  # header keys are recognized:
 
469
  #
 
470
  # type:: the Content-Type header.  Defaults to "text/html"
 
471
  # charset:: the charset of the body, appended to the Content-Type header.
 
472
  # nph:: a boolean value.  If true, prepend protocol string and status code, and
 
473
  #       date; and sets default values for "server" and "connection" if not
 
474
  #       explicitly set.
 
475
  # status:: the HTTP status code, returned as the Status header.  See the
 
476
  #          list of available status codes below.
 
477
  # server:: the server software, returned as the Server header.
 
478
  # connection:: the connection type, returned as the Connection header (for 
 
479
  #              instance, "close".
 
480
  # length:: the length of the content that will be sent, returned as the
 
481
  #          Content-Length header.
 
482
  # language:: the language of the content, returned as the Content-Language
 
483
  #            header.
 
484
  # expires:: the time on which the current content expires, as a +Time+
 
485
  #           object, returned as the Expires header.
 
486
  # cookie:: a cookie or cookies, returned as one or more Set-Cookie headers.
 
487
  #          The value can be the literal string of the cookie; a CGI::Cookie
 
488
  #          object; an Array of literal cookie strings or Cookie objects; or a 
 
489
  #          hash all of whose values are literal cookie strings or Cookie objects.
 
490
  #          These cookies are in addition to the cookies held in the
 
491
  #          @output_cookies field.
 
492
  #
 
493
  # Other header lines can also be set; they are appended as key: value.
 
494
  # 
 
495
  #   header
 
496
  #     # Content-Type: text/html
 
497
  # 
 
498
  #   header("text/plain")
 
499
  #     # Content-Type: text/plain
 
500
  # 
 
501
  #   header("nph"        => true,
 
502
  #          "status"     => "OK",  # == "200 OK"
 
503
  #            # "status"     => "200 GOOD",
 
504
  #          "server"     => ENV['SERVER_SOFTWARE'],
 
505
  #          "connection" => "close",
 
506
  #          "type"       => "text/html",
 
507
  #          "charset"    => "iso-2022-jp",
 
508
  #            # Content-Type: text/html; charset=iso-2022-jp
 
509
  #          "length"     => 103,
 
510
  #          "language"   => "ja",
 
511
  #          "expires"    => Time.now + 30,
 
512
  #          "cookie"     => [cookie1, cookie2],
 
513
  #          "my_header1" => "my_value"
 
514
  #          "my_header2" => "my_value")
 
515
  # 
 
516
  # The status codes are:
 
517
  # 
 
518
  #   "OK"                  --> "200 OK"
 
519
  #   "PARTIAL_CONTENT"     --> "206 Partial Content"
 
520
  #   "MULTIPLE_CHOICES"    --> "300 Multiple Choices"
 
521
  #   "MOVED"               --> "301 Moved Permanently"
 
522
  #   "REDIRECT"            --> "302 Found"
 
523
  #   "NOT_MODIFIED"        --> "304 Not Modified"
 
524
  #   "BAD_REQUEST"         --> "400 Bad Request"
 
525
  #   "AUTH_REQUIRED"       --> "401 Authorization Required"
 
526
  #   "FORBIDDEN"           --> "403 Forbidden"
 
527
  #   "NOT_FOUND"           --> "404 Not Found"
 
528
  #   "METHOD_NOT_ALLOWED"  --> "405 Method Not Allowed"
 
529
  #   "NOT_ACCEPTABLE"      --> "406 Not Acceptable"
 
530
  #   "LENGTH_REQUIRED"     --> "411 Length Required"
 
531
  #   "PRECONDITION_FAILED" --> "412 Precondition Failed"
 
532
  #   "SERVER_ERROR"        --> "500 Internal Server Error"
 
533
  #   "NOT_IMPLEMENTED"     --> "501 Method Not Implemented"
 
534
  #   "BAD_GATEWAY"         --> "502 Bad Gateway"
 
535
  #   "VARIANT_ALSO_VARIES" --> "506 Variant Also Negotiates"
 
536
  # 
 
537
  # This method does not perform charset conversion. 
 
538
  #
 
539
  def header(options = "text/html")
 
540
 
 
541
    buf = ""
 
542
 
 
543
    case options
 
544
    when String
 
545
      options = { "type" => options }
 
546
    when Hash
 
547
      options = options.dup
 
548
    end
 
549
 
 
550
    unless options.has_key?("type")
 
551
      options["type"] = "text/html"
 
552
    end
 
553
 
 
554
    if options.has_key?("charset")
 
555
      options["type"] += "; charset=" + options.delete("charset")
 
556
    end
 
557
 
 
558
    options.delete("nph") if defined?(MOD_RUBY)
 
559
    if options.delete("nph") or
 
560
        (/IIS\/(\d+)/n.match(env_table['SERVER_SOFTWARE']) and $1.to_i < 5)
 
561
      buf += (env_table["SERVER_PROTOCOL"] or "HTTP/1.0")  + " " +
 
562
             (HTTP_STATUS[options["status"]] or options["status"] or "200 OK") +
 
563
             EOL +
 
564
             "Date: " + CGI::rfc1123_date(Time.now) + EOL
 
565
 
 
566
      unless options.has_key?("server")
 
567
        options["server"] = (env_table['SERVER_SOFTWARE'] or "")
 
568
      end
 
569
 
 
570
      unless options.has_key?("connection")
 
571
        options["connection"] = "close"
 
572
      end
 
573
 
 
574
      options.delete("status")
 
575
    end
 
576
 
 
577
    if options.has_key?("status")
 
578
      buf += "Status: " +
 
579
             (HTTP_STATUS[options["status"]] or options["status"]) + EOL
 
580
      options.delete("status")
 
581
    end
 
582
 
 
583
    if options.has_key?("server")
 
584
      buf += "Server: " + options.delete("server") + EOL
 
585
    end
 
586
 
 
587
    if options.has_key?("connection")
 
588
      buf += "Connection: " + options.delete("connection") + EOL
 
589
    end
 
590
 
 
591
    buf += "Content-Type: " + options.delete("type") + EOL
 
592
 
 
593
    if options.has_key?("length")
 
594
      buf += "Content-Length: " + options.delete("length").to_s + EOL
 
595
    end
 
596
 
 
597
    if options.has_key?("language")
 
598
      buf += "Content-Language: " + options.delete("language") + EOL
 
599
    end
 
600
 
 
601
    if options.has_key?("expires")
 
602
      buf += "Expires: " + CGI::rfc1123_date( options.delete("expires") ) + EOL
 
603
    end
 
604
 
 
605
    if options.has_key?("cookie")
 
606
      if options["cookie"].kind_of?(String) or
 
607
           options["cookie"].kind_of?(Cookie)
 
608
        buf += "Set-Cookie: " + options.delete("cookie").to_s + EOL
 
609
      elsif options["cookie"].kind_of?(Array)
 
610
        options.delete("cookie").each{|cookie|
 
611
          buf += "Set-Cookie: " + cookie.to_s + EOL
 
612
        }
 
613
      elsif options["cookie"].kind_of?(Hash)
 
614
        options.delete("cookie").each_value{|cookie|
 
615
          buf += "Set-Cookie: " + cookie.to_s + EOL
 
616
        }
 
617
      end
 
618
    end
 
619
    if @output_cookies
 
620
      for cookie in @output_cookies
 
621
        buf += "Set-Cookie: " + cookie.to_s + EOL
 
622
      end
 
623
    end
 
624
 
 
625
    options.each{|key, value|
 
626
      buf += key + ": " + value.to_s + EOL
 
627
    }
 
628
 
 
629
    if defined?(MOD_RUBY)
 
630
      table = Apache::request.headers_out
 
631
      buf.scan(/([^:]+): (.+)#{EOL}/n){ |name, value|
 
632
        warn sprintf("name:%s value:%s\n", name, value) if $DEBUG
 
633
        case name
 
634
        when 'Set-Cookie'
 
635
          table.add(name, value)
 
636
        when /^status$/ni
 
637
          Apache::request.status_line = value
 
638
          Apache::request.status = value.to_i
 
639
        when /^content-type$/ni
 
640
          Apache::request.content_type = value
 
641
        when /^content-encoding$/ni
 
642
          Apache::request.content_encoding = value
 
643
        when /^location$/ni
 
644
          if Apache::request.status == 200
 
645
            Apache::request.status = 302
 
646
          end
 
647
          Apache::request.headers_out[name] = value
 
648
        else
 
649
          Apache::request.headers_out[name] = value
 
650
        end
 
651
      }
 
652
      Apache::request.send_http_header
 
653
      ''
 
654
    else
 
655
      buf + EOL
 
656
    end
 
657
 
 
658
  end # header()
 
659
 
 
660
 
 
661
  # Print an HTTP header and body to $DEFAULT_OUTPUT ($>)
 
662
  #
 
663
  # The header is provided by +options+, as for #header().
 
664
  # The body of the document is that returned by the passed-
 
665
  # in block.  This block takes no arguments.  It is required.
 
666
  #
 
667
  #   cgi = CGI.new
 
668
  #   cgi.out{ "string" }
 
669
  #     # Content-Type: text/html
 
670
  #     # Content-Length: 6
 
671
  #     #
 
672
  #     # string
 
673
  # 
 
674
  #   cgi.out("text/plain") { "string" }
 
675
  #     # Content-Type: text/plain
 
676
  #     # Content-Length: 6
 
677
  #     #
 
678
  #     # string
 
679
  # 
 
680
  #   cgi.out("nph"        => true,
 
681
  #           "status"     => "OK",  # == "200 OK"
 
682
  #           "server"     => ENV['SERVER_SOFTWARE'],
 
683
  #           "connection" => "close",
 
684
  #           "type"       => "text/html",
 
685
  #           "charset"    => "iso-2022-jp",
 
686
  #             # Content-Type: text/html; charset=iso-2022-jp
 
687
  #           "language"   => "ja",
 
688
  #           "expires"    => Time.now + (3600 * 24 * 30),
 
689
  #           "cookie"     => [cookie1, cookie2],
 
690
  #           "my_header1" => "my_value",
 
691
  #           "my_header2" => "my_value") { "string" }
 
692
  # 
 
693
  # Content-Length is automatically calculated from the size of
 
694
  # the String returned by the content block.
 
695
  #
 
696
  # If ENV['REQUEST_METHOD'] == "HEAD", then only the header
 
697
  # is outputted (the content block is still required, but it
 
698
  # is ignored).
 
699
  # 
 
700
  # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then
 
701
  # the content is converted to this charset, and the language is set 
 
702
  # to "ja".
 
703
  def out(options = "text/html") # :yield:
 
704
 
 
705
    options = { "type" => options } if options.kind_of?(String)
 
706
    content = yield
 
707
 
 
708
    if options.has_key?("charset")
 
709
      require "nkf"
 
710
      case options["charset"]
 
711
      when /iso-2022-jp/ni
 
712
        content = NKF::nkf('-m0 -x -j', content)
 
713
        options["language"] = "ja" unless options.has_key?("language")
 
714
      when /euc-jp/ni
 
715
        content = NKF::nkf('-m0 -x -e', content)
 
716
        options["language"] = "ja" unless options.has_key?("language")
 
717
      when /shift_jis/ni
 
718
        content = NKF::nkf('-m0 -x -s', content)
 
719
        options["language"] = "ja" unless options.has_key?("language")
 
720
      end
 
721
    end
 
722
 
 
723
    options["length"] = content.length.to_s
 
724
    output = stdoutput
 
725
    output.binmode if defined? output.binmode
 
726
    output.print header(options)
 
727
    output.print content unless "HEAD" == env_table['REQUEST_METHOD']
 
728
  end
 
729
 
 
730
 
 
731
  # Print an argument or list of arguments to the default output stream
 
732
  #
 
733
  #   cgi = CGI.new
 
734
  #   cgi.print    # default:  cgi.print == $DEFAULT_OUTPUT.print
 
735
  def print(*options)
 
736
    stdoutput.print(*options)
 
737
  end
 
738
 
 
739
  require "delegate"
 
740
 
 
741
  # Class representing an HTTP cookie.
 
742
  #
 
743
  # In addition to its specific fields and methods, a Cookie instance
 
744
  # is a delegator to the array of its values.
 
745
  #
 
746
  # See RFC 2965.
 
747
  #
 
748
  # == Examples of use
 
749
  #   cookie1 = CGI::Cookie::new("name", "value1", "value2", ...)
 
750
  #   cookie1 = CGI::Cookie::new("name" => "name", "value" => "value")
 
751
  #   cookie1 = CGI::Cookie::new('name'    => 'name',
 
752
  #                              'value'   => ['value1', 'value2', ...],
 
753
  #                              'path'    => 'path',   # optional
 
754
  #                              'domain'  => 'domain', # optional
 
755
  #                              'expires' => Time.now, # optional
 
756
  #                              'secure'  => true      # optional
 
757
  #                             )
 
758
  # 
 
759
  #   cgi.out("cookie" => [cookie1, cookie2]) { "string" }
 
760
  # 
 
761
  #   name    = cookie1.name
 
762
  #   values  = cookie1.value
 
763
  #   path    = cookie1.path
 
764
  #   domain  = cookie1.domain
 
765
  #   expires = cookie1.expires
 
766
  #   secure  = cookie1.secure
 
767
  # 
 
768
  #   cookie1.name    = 'name'
 
769
  #   cookie1.value   = ['value1', 'value2', ...]
 
770
  #   cookie1.path    = 'path'
 
771
  #   cookie1.domain  = 'domain'
 
772
  #   cookie1.expires = Time.now + 30
 
773
  #   cookie1.secure  = true
 
774
  class Cookie < DelegateClass(Array)
 
775
 
 
776
    # Create a new CGI::Cookie object.
 
777
    #
 
778
    # The contents of the cookie can be specified as a +name+ and one
 
779
    # or more +value+ arguments.  Alternatively, the contents can
 
780
    # be specified as a single hash argument.  The possible keywords of
 
781
    # this hash are as follows:
 
782
    #
 
783
    # name:: the name of the cookie.  Required.
 
784
    # value:: the cookie's value or list of values.
 
785
    # path:: the path for which this cookie applies.  Defaults to the
 
786
    #        base directory of the CGI script.
 
787
    # domain:: the domain for which this cookie applies.
 
788
    # expires:: the time at which this cookie expires, as a +Time+ object.
 
789
    # secure:: whether this cookie is a secure cookie or not (default to
 
790
    #          false).  Secure cookies are only transmitted to HTTPS 
 
791
    #          servers.
 
792
    #
 
793
    # These keywords correspond to attributes of the cookie object.
 
794
    def initialize(name = "", *value)
 
795
      options = if name.kind_of?(String)
 
796
                  { "name" => name, "value" => value }
 
797
                else
 
798
                  name
 
799
                end
 
800
      unless options.has_key?("name")
 
801
        raise ArgumentError, "`name' required"
 
802
      end
 
803
 
 
804
      @name = options["name"]
 
805
      @value = Array(options["value"])
 
806
      # simple support for IE
 
807
      if options["path"]
 
808
        @path = options["path"]
 
809
      else
 
810
        %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
 
811
        @path = ($1 or "")
 
812
      end
 
813
      @domain = options["domain"]
 
814
      @expires = options["expires"]
 
815
      @secure = options["secure"] == true ? true : false
 
816
 
 
817
      super(@value)
 
818
    end
 
819
 
 
820
    attr_accessor("name", "value", "path", "domain", "expires")
 
821
    attr_reader("secure")
 
822
 
 
823
    # Set whether the Cookie is a secure cookie or not.
 
824
    #
 
825
    # +val+ must be a boolean.
 
826
    def secure=(val)
 
827
      @secure = val if val == true or val == false
 
828
      @secure
 
829
    end
 
830
 
 
831
    # Convert the Cookie to its string representation.
 
832
    def to_s
 
833
      buf = ""
 
834
      buf += @name + '='
 
835
 
 
836
      if @value.kind_of?(String)
 
837
        buf += CGI::escape(@value)
 
838
      else
 
839
        buf += @value.collect{|v| CGI::escape(v) }.join("&")
 
840
      end
 
841
 
 
842
      if @domain
 
843
        buf += '; domain=' + @domain
 
844
      end
 
845
 
 
846
      if @path
 
847
        buf += '; path=' + @path
 
848
      end
 
849
 
 
850
      if @expires
 
851
        buf += '; expires=' + CGI::rfc1123_date(@expires)
 
852
      end
 
853
 
 
854
      if @secure == true
 
855
        buf += '; secure'
 
856
      end
 
857
 
 
858
      buf
 
859
    end
 
860
 
 
861
  end # class Cookie
 
862
 
 
863
 
 
864
  # Parse a raw cookie string into a hash of cookie-name=>Cookie
 
865
  # pairs.
 
866
  #
 
867
  #   cookies = CGI::Cookie::parse("raw_cookie_string")
 
868
  #     # { "name1" => cookie1, "name2" => cookie2, ... }
 
869
  #
 
870
  def Cookie::parse(raw_cookie)
 
871
    cookies = Hash.new([])
 
872
    return cookies unless raw_cookie
 
873
 
 
874
    raw_cookie.split(/[;,]\s?/).each do |pairs|
 
875
      name, values = pairs.split('=',2)
 
876
      next unless name and values
 
877
      name = CGI::unescape(name)
 
878
      values ||= ""
 
879
      values = values.split('&').collect{|v| CGI::unescape(v) }
 
880
      if cookies.has_key?(name)
 
881
        values = cookies[name].value + values
 
882
      end
 
883
      cookies[name] = Cookie::new({ "name" => name, "value" => values })
 
884
    end
 
885
 
 
886
    cookies
 
887
  end
 
888
 
 
889
  # Parse an HTTP query string into a hash of key=>value pairs.
 
890
  #
 
891
  #   params = CGI::parse("query_string")
 
892
  #     # {"name1" => ["value1", "value2", ...],
 
893
  #     #  "name2" => ["value1", "value2", ...], ... }
 
894
  #
 
895
  def CGI::parse(query)
 
896
    params = Hash.new([].freeze)
 
897
 
 
898
    query.split(/[&;]/n).each do |pairs|
 
899
      key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) }
 
900
      if params.has_key?(key)
 
901
        params[key].push(value)
 
902
      else
 
903
        params[key] = [value]
 
904
      end
 
905
    end
 
906
 
 
907
    params
 
908
  end
 
909
 
 
910
  # Mixin module. It provides the follow functionality groups:
 
911
  #
 
912
  # 1. Access to CGI environment variables as methods.  See 
 
913
  #    documentation to the CGI class for a list of these variables.
 
914
  #
 
915
  # 2. Access to cookies, including the cookies attribute.
 
916
  #
 
917
  # 3. Access to parameters, including the params attribute, and overloading
 
918
  #    [] to perform parameter value lookup by key.
 
919
  #
 
920
  # 4. The initialize_query method, for initialising the above
 
921
  #    mechanisms, handling multipart forms, and allowing the
 
922
  #    class to be used in "offline" mode.
 
923
  #
 
924
  module QueryExtension
 
925
 
 
926
    %w[ CONTENT_LENGTH SERVER_PORT ].each do |env|
 
927
      define_method(env.sub(/^HTTP_/n, '').downcase) do
 
928
        (val = env_table[env]) && Integer(val)
 
929
      end
 
930
    end
 
931
 
 
932
    %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
 
933
        PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST
 
934
        REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME
 
935
        SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE
 
936
 
 
937
        HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
 
938
        HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
 
939
        HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
 
940
      define_method(env.sub(/^HTTP_/n, '').downcase) do
 
941
        env_table[env]
 
942
      end
 
943
    end
 
944
 
 
945
    # Get the raw cookies as a string.
 
946
    def raw_cookie
 
947
      env_table["HTTP_COOKIE"]
 
948
    end
 
949
 
 
950
    # Get the raw RFC2965 cookies as a string.
 
951
    def raw_cookie2
 
952
      env_table["HTTP_COOKIE2"]
 
953
    end
 
954
 
 
955
    # Get the cookies as a hash of cookie-name=>Cookie pairs.
 
956
    attr_accessor("cookies")
 
957
 
 
958
    # Get the parameters as a hash of name=>values pairs, where
 
959
    # values is an Array.
 
960
    attr("params")
 
961
 
 
962
    # Set all the parameters.
 
963
    def params=(hash)
 
964
      @params.clear
 
965
      @params.update(hash)
 
966
    end
 
967
 
 
968
    def read_multipart(boundary, content_length)
 
969
      params = Hash.new([])
 
970
      boundary = "--" + boundary
 
971
      quoted_boundary = Regexp.quote(boundary, "n")
 
972
      buf = ""
 
973
      bufsize = 10 * 1024
 
974
      boundary_end=""
 
975
 
 
976
      # start multipart/form-data
 
977
      stdinput.binmode if defined? stdinput.binmode
 
978
      boundary_size = boundary.size + EOL.size
 
979
      content_length -= boundary_size
 
980
      status = stdinput.read(boundary_size)
 
981
      if nil == status
 
982
        raise EOFError, "no content body"
 
983
      elsif boundary + EOL != status
 
984
        raise EOFError, "bad content body"
 
985
      end
 
986
 
 
987
      loop do
 
988
        head = nil
 
989
        if 10240 < content_length
 
990
          require "tempfile"
 
991
          body = Tempfile.new("CGI")
 
992
        else
 
993
          begin
 
994
            require "stringio"
 
995
            body = StringIO.new
 
996
          rescue LoadError
 
997
            require "tempfile"
 
998
            body = Tempfile.new("CGI")
 
999
          end
 
1000
        end
 
1001
        body.binmode if defined? body.binmode
 
1002
 
 
1003
        until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
 
1004
 
 
1005
          if (not head) and /#{EOL}#{EOL}/n.match(buf)
 
1006
            buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
 
1007
              head = $1.dup
 
1008
              ""
 
1009
            end
 
1010
            next
 
1011
          end
 
1012
 
 
1013
          if head and ( (EOL + boundary + EOL).size < buf.size )
 
1014
            body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
 
1015
            buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
 
1016
          end
 
1017
 
 
1018
          c = if bufsize < content_length
 
1019
                stdinput.read(bufsize)
 
1020
              else
 
1021
                stdinput.read(content_length)
 
1022
              end
 
1023
          if c.nil? || c.empty?
 
1024
            raise EOFError, "bad content body"
 
1025
          end
 
1026
          buf.concat(c)
 
1027
          content_length -= c.size
 
1028
        end
 
1029
 
 
1030
        buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
 
1031
          body.print $1
 
1032
          if "--" == $2
 
1033
            content_length = -1
 
1034
          end
 
1035
         boundary_end = $2.dup
 
1036
          ""
 
1037
        end
 
1038
 
 
1039
        body.rewind
 
1040
 
 
1041
        /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni.match(head)
 
1042
        filename = ($1 or $2 or "")
 
1043
        if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
 
1044
            /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
 
1045
            (not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
 
1046
          filename = CGI::unescape(filename)
 
1047
        end
 
1048
        
 
1049
        /Content-Type: (.*)/ni.match(head)
 
1050
        content_type = ($1 or "")
 
1051
 
 
1052
        (class << body; self; end).class_eval do
 
1053
          alias local_path path
 
1054
          define_method(:original_filename) {filename.dup.taint}
 
1055
          define_method(:content_type) {content_type.dup.taint}
 
1056
        end
 
1057
 
 
1058
        /Content-Disposition:.* name="?([^\";]*)"?/ni.match(head)
 
1059
        name = $1.dup
 
1060
 
 
1061
        if params.has_key?(name)
 
1062
          params[name].push(body)
 
1063
        else
 
1064
          params[name] = [body]
 
1065
        end
 
1066
        break if buf.size == 0
 
1067
        break if content_length == -1
 
1068
      end
 
1069
      raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
 
1070
 
 
1071
      params
 
1072
    end # read_multipart
 
1073
    private :read_multipart
 
1074
 
 
1075
    # offline mode. read name=value pairs on standard input.
 
1076
    def read_from_cmdline
 
1077
      require "shellwords"
 
1078
 
 
1079
      string = unless ARGV.empty?
 
1080
        ARGV.join(' ')
 
1081
      else
 
1082
        if STDIN.tty?
 
1083
          STDERR.print(
 
1084
            %|(offline mode: enter name=value pairs on standard input)\n|
 
1085
          )
 
1086
        end
 
1087
        readlines.join(' ').gsub(/\n/n, '')
 
1088
      end.gsub(/\\=/n, '%3D').gsub(/\\&/n, '%26')
 
1089
 
 
1090
      words = Shellwords.shellwords(string)
 
1091
 
 
1092
      if words.find{|x| /=/n.match(x) }
 
1093
        words.join('&')
 
1094
      else
 
1095
        words.join('+')
 
1096
      end
 
1097
    end
 
1098
    private :read_from_cmdline
 
1099
 
 
1100
    # Initialize the data from the query.
 
1101
    #
 
1102
    # Handles multipart forms (in particular, forms that involve file uploads).
 
1103
    # Reads query parameters in the @params field, and cookies into @cookies.
 
1104
    def initialize_query()
 
1105
      if ("POST" == env_table['REQUEST_METHOD']) and
 
1106
         %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(env_table['CONTENT_TYPE'])
 
1107
        boundary = $1.dup
 
1108
        @multipart = true
 
1109
        @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
 
1110
      else
 
1111
        @multipart = false
 
1112
        @params = CGI::parse(
 
1113
                    case env_table['REQUEST_METHOD']
 
1114
                    when "GET", "HEAD"
 
1115
                      if defined?(MOD_RUBY)
 
1116
                        Apache::request.args or ""
 
1117
                      else
 
1118
                        env_table['QUERY_STRING'] or ""
 
1119
                      end
 
1120
                    when "POST"
 
1121
                      stdinput.binmode if defined? stdinput.binmode
 
1122
                      stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
 
1123
                    else
 
1124
                      read_from_cmdline
 
1125
                    end
 
1126
                  )
 
1127
      end
 
1128
 
 
1129
      @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
 
1130
    end
 
1131
    private :initialize_query
 
1132
 
 
1133
    def multipart?
 
1134
      @multipart
 
1135
    end
 
1136
 
 
1137
    module Value    # :nodoc:
 
1138
      def set_params(params)
 
1139
        @params = params
 
1140
      end
 
1141
      def [](idx, *args)
 
1142
        if args.size == 0
 
1143
          warn "#{caller(1)[0]}:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array, use cgi.params['key']"
 
1144
          @params[idx]
 
1145
        else
 
1146
          super[idx,*args]
 
1147
        end
 
1148
      end
 
1149
      def first
 
1150
        warn "#{caller(1)[0]}:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array, use cgi.params['key']"
 
1151
        self
 
1152
      end
 
1153
      alias last first
 
1154
      def to_a
 
1155
        @params || [self]
 
1156
      end
 
1157
      alias to_ary to_a         # to be rhs of multiple assignment
 
1158
    end
 
1159
 
 
1160
    # Get the value for the parameter with a given key.
 
1161
    #
 
1162
    # If the parameter has multiple values, only the first will be 
 
1163
    # retrieved; use #params() to get the array of values.
 
1164
    def [](key)
 
1165
      params = @params[key]
 
1166
      value = params[0]
 
1167
      if @multipart
 
1168
        if value
 
1169
          return value
 
1170
        elsif defined? StringIO
 
1171
          StringIO.new("")
 
1172
        else
 
1173
          Tempfile.new("CGI")
 
1174
        end
 
1175
      else
 
1176
        str = if value then value.dup else "" end
 
1177
        str.extend(Value)
 
1178
        str.set_params(params)
 
1179
        str
 
1180
      end
 
1181
    end
 
1182
 
 
1183
    # Return all parameter keys as an array.
 
1184
    def keys(*args)
 
1185
      @params.keys(*args)
 
1186
    end
 
1187
 
 
1188
    # Returns true if a given parameter key exists in the query.
 
1189
    def has_key?(*args)
 
1190
      @params.has_key?(*args)
 
1191
    end
 
1192
    alias key? has_key?
 
1193
    alias include? has_key?
 
1194
 
 
1195
  end # QueryExtension
 
1196
 
 
1197
 
 
1198
  # Prettify (indent) an HTML string.
 
1199
  #
 
1200
  # +string+ is the HTML string to indent.  +shift+ is the indentation
 
1201
  # unit to use; it defaults to two spaces.
 
1202
  #
 
1203
  #   print CGI::pretty("<HTML><BODY></BODY></HTML>")
 
1204
  #     # <HTML>
 
1205
  #     #   <BODY>
 
1206
  #     #   </BODY>
 
1207
  #     # </HTML>
 
1208
  # 
 
1209
  #   print CGI::pretty("<HTML><BODY></BODY></HTML>", "\t")
 
1210
  #     # <HTML>
 
1211
  #     #         <BODY>
 
1212
  #     #         </BODY>
 
1213
  #     # </HTML>
 
1214
  #
 
1215
  def CGI::pretty(string, shift = "  ")
 
1216
    lines = string.gsub(/(?!\A)<(?:.|\n)*?>/n, "\n\\0").gsub(/<(?:.|\n)*?>(?!\n)/n, "\\0\n")
 
1217
    end_pos = 0
 
1218
    while end_pos = lines.index(/^<\/(\w+)/n, end_pos)
 
1219
      element = $1.dup
 
1220
      start_pos = lines.rindex(/^\s*<#{element}/ni, end_pos)
 
1221
      lines[start_pos ... end_pos] = "__" + lines[start_pos ... end_pos].gsub(/\n(?!\z)/n, "\n" + shift) + "__"
 
1222
    end
 
1223
    lines.gsub(/^((?:#{Regexp::quote(shift)})*)__(?=<\/?\w)/n, '\1')
 
1224
  end
 
1225
 
 
1226
 
 
1227
  # Base module for HTML-generation mixins.
 
1228
  #
 
1229
  # Provides methods for code generation for tags following
 
1230
  # the various DTD element types.
 
1231
  module TagMaker # :nodoc:
 
1232
 
 
1233
    # Generate code for an element with required start and end tags.
 
1234
    #
 
1235
    #   - -
 
1236
    def nn_element_def(element)
 
1237
      nOE_element_def(element, <<-END)
 
1238
          if block_given?
 
1239
            yield.to_s
 
1240
          else
 
1241
            ""
 
1242
          end +
 
1243
          "</#{element.upcase}>"
 
1244
      END
 
1245
    end
 
1246
 
 
1247
    # Generate code for an empty element.
 
1248
    #
 
1249
    #   - O EMPTY
 
1250
    def nOE_element_def(element, append = nil)
 
1251
      s = <<-END
 
1252
          "<#{element.upcase}" + attributes.collect{|name, value|
 
1253
            next unless value
 
1254
            " " + CGI::escapeHTML(name) +
 
1255
            if true == value
 
1256
              ""
 
1257
            else
 
1258
              '="' + CGI::escapeHTML(value) + '"'
 
1259
            end
 
1260
          }.to_s + ">"
 
1261
      END
 
1262
      s.sub!(/\Z/, " +") << append if append
 
1263
      s
 
1264
    end
 
1265
 
 
1266
    # Generate code for an element for which the end (and possibly the
 
1267
    # start) tag is optional.
 
1268
    #
 
1269
    #   O O or - O
 
1270
    def nO_element_def(element)
 
1271
      nOE_element_def(element, <<-END)
 
1272
          if block_given?
 
1273
            yield.to_s + "</#{element.upcase}>"
 
1274
          else
 
1275
            ""
 
1276
          end
 
1277
      END
 
1278
    end
 
1279
 
 
1280
  end # TagMaker
 
1281
 
 
1282
 
 
1283
  #
 
1284
  # Mixin module providing HTML generation methods.
 
1285
  #
 
1286
  # For example,
 
1287
  #   cgi.a("http://www.example.com") { "Example" }
 
1288
  #     # => "<A HREF=\"http://www.example.com\">Example</A>"
 
1289
  #
 
1290
  # Modules Http3, Http4, etc., contain more basic HTML-generation methods
 
1291
  # (:title, :center, etc.).
 
1292
  #
 
1293
  # See class CGI for a detailed example. 
 
1294
  #
 
1295
  module HtmlExtension
 
1296
 
 
1297
 
 
1298
    # Generate an Anchor element as a string.
 
1299
    #
 
1300
    # +href+ can either be a string, giving the URL
 
1301
    # for the HREF attribute, or it can be a hash of
 
1302
    # the element's attributes.
 
1303
    #
 
1304
    # The body of the element is the string returned by the no-argument
 
1305
    # block passed in.
 
1306
    #
 
1307
    #   a("http://www.example.com") { "Example" }
 
1308
    #     # => "<A HREF=\"http://www.example.com\">Example</A>"
 
1309
    #
 
1310
    #   a("HREF" => "http://www.example.com", "TARGET" => "_top") { "Example" }
 
1311
    #     # => "<A HREF=\"http://www.example.com\" TARGET=\"_top\">Example</A>"
 
1312
    #
 
1313
    def a(href = "") # :yield:
 
1314
      attributes = if href.kind_of?(String)
 
1315
                     { "HREF" => href }
 
1316
                   else
 
1317
                     href
 
1318
                   end
 
1319
      if block_given?
 
1320
        super(attributes){ yield }
 
1321
      else
 
1322
        super(attributes)
 
1323
      end
 
1324
    end
 
1325
 
 
1326
    # Generate a Document Base URI element as a String. 
 
1327
    #
 
1328
    # +href+ can either by a string, giving the base URL for the HREF
 
1329
    # attribute, or it can be a has of the element's attributes.
 
1330
    #
 
1331
    # The passed-in no-argument block is ignored.
 
1332
    #
 
1333
    #   base("http://www.example.com/cgi")
 
1334
    #     # => "<BASE HREF=\"http://www.example.com/cgi\">"
 
1335
    def base(href = "") # :yield:
 
1336
      attributes = if href.kind_of?(String)
 
1337
                     { "HREF" => href }
 
1338
                   else
 
1339
                     href
 
1340
                   end
 
1341
      if block_given?
 
1342
        super(attributes){ yield }
 
1343
      else
 
1344
        super(attributes)
 
1345
      end
 
1346
    end
 
1347
 
 
1348
    # Generate a BlockQuote element as a string.
 
1349
    #
 
1350
    # +cite+ can either be a string, give the URI for the source of
 
1351
    # the quoted text, or a hash, giving all attributes of the element,
 
1352
    # or it can be omitted, in which case the element has no attributes.
 
1353
    #
 
1354
    # The body is provided by the passed-in no-argument block
 
1355
    #
 
1356
    #   blockquote("http://www.example.com/quotes/foo.html") { "Foo!" }
 
1357
    #     #=> "<BLOCKQUOTE CITE=\"http://www.example.com/quotes/foo.html\">Foo!</BLOCKQUOTE>
 
1358
    def blockquote(cite = nil)  # :yield:
 
1359
      attributes = if cite.kind_of?(String)
 
1360
                     { "CITE" => cite }
 
1361
                   else
 
1362
                     cite or ""
 
1363
                   end
 
1364
      if block_given?
 
1365
        super(attributes){ yield }
 
1366
      else
 
1367
        super(attributes)
 
1368
      end
 
1369
    end
 
1370
 
 
1371
 
 
1372
    # Generate a Table Caption element as a string.
 
1373
    #
 
1374
    # +align+ can be a string, giving the alignment of the caption
 
1375
    # (one of top, bottom, left, or right).  It can be a hash of
 
1376
    # all the attributes of the element.  Or it can be omitted.
 
1377
    #
 
1378
    # The body of the element is provided by the passed-in no-argument block.
 
1379
    #
 
1380
    #   caption("left") { "Capital Cities" }
 
1381
    #     # => <CAPTION ALIGN=\"left\">Capital Cities</CAPTION>
 
1382
    def caption(align = nil) # :yield:
 
1383
      attributes = if align.kind_of?(String)
 
1384
                     { "ALIGN" => align }
 
1385
                   else
 
1386
                     align or ""
 
1387
                   end
 
1388
      if block_given?
 
1389
        super(attributes){ yield }
 
1390
      else
 
1391
        super(attributes)
 
1392
      end
 
1393
    end
 
1394
 
 
1395
 
 
1396
    # Generate a Checkbox Input element as a string.
 
1397
    #
 
1398
    # The attributes of the element can be specified as three arguments,
 
1399
    # +name+, +value+, and +checked+.  +checked+ is a boolean value;
 
1400
    # if true, the CHECKED attribute will be included in the element.
 
1401
    #
 
1402
    # Alternatively, the attributes can be specified as a hash.
 
1403
    #
 
1404
    #   checkbox("name")
 
1405
    #     # = checkbox("NAME" => "name")
 
1406
    # 
 
1407
    #   checkbox("name", "value")
 
1408
    #     # = checkbox("NAME" => "name", "VALUE" => "value")
 
1409
    # 
 
1410
    #   checkbox("name", "value", true)
 
1411
    #     # = checkbox("NAME" => "name", "VALUE" => "value", "CHECKED" => true)
 
1412
    def checkbox(name = "", value = nil, checked = nil)
 
1413
      attributes = if name.kind_of?(String)
 
1414
                     { "TYPE" => "checkbox", "NAME" => name,
 
1415
                       "VALUE" => value, "CHECKED" => checked }
 
1416
                   else
 
1417
                     name["TYPE"] = "checkbox"
 
1418
                     name
 
1419
                   end
 
1420
      input(attributes)
 
1421
    end
 
1422
 
 
1423
    # Generate a sequence of checkbox elements, as a String.
 
1424
    #
 
1425
    # The checkboxes will all have the same +name+ attribute.
 
1426
    # Each checkbox is followed by a label.
 
1427
    # There will be one checkbox for each value.  Each value
 
1428
    # can be specified as a String, which will be used both
 
1429
    # as the value of the VALUE attribute and as the label
 
1430
    # for that checkbox.  A single-element array has the
 
1431
    # same effect.
 
1432
    #
 
1433
    # Each value can also be specified as a three-element array.
 
1434
    # The first element is the VALUE attribute; the second is the
 
1435
    # label; and the third is a boolean specifying whether this
 
1436
    # checkbox is CHECKED.
 
1437
    #
 
1438
    # Each value can also be specified as a two-element
 
1439
    # array, by omitting either the value element (defaults
 
1440
    # to the same as the label), or the boolean checked element
 
1441
    # (defaults to false).
 
1442
    #
 
1443
    #   checkbox_group("name", "foo", "bar", "baz")
 
1444
    #     # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
 
1445
    #     # <INPUT TYPE="checkbox" NAME="name" VALUE="bar">bar
 
1446
    #     # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
 
1447
    # 
 
1448
    #   checkbox_group("name", ["foo"], ["bar", true], "baz")
 
1449
    #     # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
 
1450
    #     # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="bar">bar
 
1451
    #     # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
 
1452
    # 
 
1453
    #   checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
 
1454
    #     # <INPUT TYPE="checkbox" NAME="name" VALUE="1">Foo
 
1455
    #     # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="2">Bar
 
1456
    #     # <INPUT TYPE="checkbox" NAME="name" VALUE="Baz">Baz
 
1457
    # 
 
1458
    #   checkbox_group("NAME" => "name",
 
1459
    #                    "VALUES" => ["foo", "bar", "baz"])
 
1460
    # 
 
1461
    #   checkbox_group("NAME" => "name",
 
1462
    #                    "VALUES" => [["foo"], ["bar", true], "baz"])
 
1463
    # 
 
1464
    #   checkbox_group("NAME" => "name",
 
1465
    #                    "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
 
1466
    def checkbox_group(name = "", *values)
 
1467
      if name.kind_of?(Hash)
 
1468
        values = name["VALUES"]
 
1469
        name = name["NAME"]
 
1470
      end
 
1471
      values.collect{|value|
 
1472
        if value.kind_of?(String)
 
1473
          checkbox(name, value) + value
 
1474
        else
 
1475
          if value[value.size - 1] == true
 
1476
            checkbox(name, value[0], true) +
 
1477
            value[value.size - 2]
 
1478
          else
 
1479
            checkbox(name, value[0]) +
 
1480
            value[value.size - 1]
 
1481
          end
 
1482
        end
 
1483
      }.to_s
 
1484
    end
 
1485
 
 
1486
 
 
1487
    # Generate an File Upload Input element as a string.
 
1488
    #
 
1489
    # The attributes of the element can be specified as three arguments,
 
1490
    # +name+, +size+, and +maxlength+.  +maxlength+ is the maximum length
 
1491
    # of the file's _name_, not of the file's _contents_.
 
1492
    #
 
1493
    # Alternatively, the attributes can be specified as a hash.
 
1494
    #
 
1495
    # See #multipart_form() for forms that include file uploads.
 
1496
    #
 
1497
    #   file_field("name")
 
1498
    #     # <INPUT TYPE="file" NAME="name" SIZE="20">
 
1499
    # 
 
1500
    #   file_field("name", 40)
 
1501
    #     # <INPUT TYPE="file" NAME="name" SIZE="40">
 
1502
    # 
 
1503
    #   file_field("name", 40, 100)
 
1504
    #     # <INPUT TYPE="file" NAME="name" SIZE="40" MAXLENGTH="100">
 
1505
    # 
 
1506
    #   file_field("NAME" => "name", "SIZE" => 40)
 
1507
    #     # <INPUT TYPE="file" NAME="name" SIZE="40">
 
1508
    def file_field(name = "", size = 20, maxlength = nil)
 
1509
      attributes = if name.kind_of?(String)
 
1510
                     { "TYPE" => "file", "NAME" => name,
 
1511
                       "SIZE" => size.to_s }
 
1512
                   else
 
1513
                     name["TYPE"] = "file"
 
1514
                     name
 
1515
                   end
 
1516
      attributes["MAXLENGTH"] = maxlength.to_s if maxlength
 
1517
      input(attributes)
 
1518
    end
 
1519
 
 
1520
 
 
1521
    # Generate a Form element as a string.
 
1522
    #
 
1523
    # +method+ should be either "get" or "post", and defaults to the latter.
 
1524
    # +action+ defaults to the current CGI script name.  +enctype+
 
1525
    # defaults to "application/x-www-form-urlencoded".  
 
1526
    #
 
1527
    # Alternatively, the attributes can be specified as a hash.
 
1528
    #
 
1529
    # See also #multipart_form() for forms that include file uploads.
 
1530
    #
 
1531
    #   form{ "string" }
 
1532
    #     # <FORM METHOD="post" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
 
1533
    # 
 
1534
    #   form("get") { "string" }
 
1535
    #     # <FORM METHOD="get" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
 
1536
    # 
 
1537
    #   form("get", "url") { "string" }
 
1538
    #     # <FORM METHOD="get" ACTION="url" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
 
1539
    # 
 
1540
    #   form("METHOD" => "post", "ENCTYPE" => "enctype") { "string" }
 
1541
    #     # <FORM METHOD="post" ENCTYPE="enctype">string</FORM>
 
1542
    def form(method = "post", action = script_name, enctype = "application/x-www-form-urlencoded")
 
1543
      attributes = if method.kind_of?(String)
 
1544
                     { "METHOD" => method, "ACTION" => action,
 
1545
                       "ENCTYPE" => enctype } 
 
1546
                   else
 
1547
                     unless method.has_key?("METHOD")
 
1548
                       method["METHOD"] = "post"
 
1549
                     end
 
1550
                     unless method.has_key?("ENCTYPE")
 
1551
                       method["ENCTYPE"] = enctype
 
1552
                     end
 
1553
                     method
 
1554
                   end
 
1555
      if block_given?
 
1556
        body = yield
 
1557
      else
 
1558
        body = ""
 
1559
      end
 
1560
      if @output_hidden
 
1561
        body += @output_hidden.collect{|k,v|
 
1562
          "<INPUT TYPE=\"HIDDEN\" NAME=\"#{k}\" VALUE=\"#{v}\">"
 
1563
        }.to_s
 
1564
      end
 
1565
      super(attributes){body}
 
1566
    end
 
1567
 
 
1568
    # Generate a Hidden Input element as a string.
 
1569
    #
 
1570
    # The attributes of the element can be specified as two arguments,
 
1571
    # +name+ and +value+.
 
1572
    #
 
1573
    # Alternatively, the attributes can be specified as a hash.
 
1574
    #
 
1575
    #   hidden("name")
 
1576
    #     # <INPUT TYPE="hidden" NAME="name">
 
1577
    # 
 
1578
    #   hidden("name", "value")
 
1579
    #     # <INPUT TYPE="hidden" NAME="name" VALUE="value">
 
1580
    # 
 
1581
    #   hidden("NAME" => "name", "VALUE" => "reset", "ID" => "foo")
 
1582
    #     # <INPUT TYPE="hidden" NAME="name" VALUE="value" ID="foo">
 
1583
    def hidden(name = "", value = nil)
 
1584
      attributes = if name.kind_of?(String)
 
1585
                     { "TYPE" => "hidden", "NAME" => name, "VALUE" => value }
 
1586
                   else
 
1587
                     name["TYPE"] = "hidden"
 
1588
                     name
 
1589
                   end
 
1590
      input(attributes)
 
1591
    end
 
1592
 
 
1593
    # Generate a top-level HTML element as a string.
 
1594
    #
 
1595
    # The attributes of the element are specified as a hash.  The
 
1596
    # pseudo-attribute "PRETTY" can be used to specify that the generated
 
1597
    # HTML string should be indented.  "PRETTY" can also be specified as
 
1598
    # a string as the sole argument to this method.  The pseudo-attribute
 
1599
    # "DOCTYPE", if given, is used as the leading DOCTYPE SGML tag; it
 
1600
    # should include the entire text of this tag, including angle brackets.
 
1601
    #
 
1602
    # The body of the html element is supplied as a block.
 
1603
    # 
 
1604
    #   html{ "string" }
 
1605
    #     # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>string</HTML>
 
1606
    # 
 
1607
    #   html("LANG" => "ja") { "string" }
 
1608
    #     # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML LANG="ja">string</HTML>
 
1609
    # 
 
1610
    #   html("DOCTYPE" => false) { "string" }
 
1611
    #     # <HTML>string</HTML>
 
1612
    # 
 
1613
    #   html("DOCTYPE" => '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">') { "string" }
 
1614
    #     # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><HTML>string</HTML>
 
1615
    # 
 
1616
    #   html("PRETTY" => "  ") { "<BODY></BODY>" }
 
1617
    #     # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
 
1618
    #     # <HTML>
 
1619
    #     #   <BODY>
 
1620
    #     #   </BODY>
 
1621
    #     # </HTML>
 
1622
    # 
 
1623
    #   html("PRETTY" => "\t") { "<BODY></BODY>" }
 
1624
    #     # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
 
1625
    #     # <HTML>
 
1626
    #     #         <BODY>
 
1627
    #     #         </BODY>
 
1628
    #     # </HTML>
 
1629
    # 
 
1630
    #   html("PRETTY") { "<BODY></BODY>" }
 
1631
    #     # = html("PRETTY" => "  ") { "<BODY></BODY>" }
 
1632
    # 
 
1633
    #   html(if $VERBOSE then "PRETTY" end) { "HTML string" }
 
1634
    #
 
1635
    def html(attributes = {}) # :yield:
 
1636
      if nil == attributes
 
1637
        attributes = {}
 
1638
      elsif "PRETTY" == attributes
 
1639
        attributes = { "PRETTY" => true }
 
1640
      end
 
1641
      pretty = attributes.delete("PRETTY")
 
1642
      pretty = "  " if true == pretty
 
1643
      buf = ""
 
1644
 
 
1645
      if attributes.has_key?("DOCTYPE")
 
1646
        if attributes["DOCTYPE"]
 
1647
          buf += attributes.delete("DOCTYPE")
 
1648
        else
 
1649
          attributes.delete("DOCTYPE")
 
1650
        end
 
1651
      else
 
1652
        buf += doctype
 
1653
      end
 
1654
 
 
1655
      if block_given?
 
1656
        buf += super(attributes){ yield }
 
1657
      else
 
1658
        buf += super(attributes)
 
1659
      end
 
1660
 
 
1661
      if pretty
 
1662
        CGI::pretty(buf, pretty)
 
1663
      else
 
1664
        buf
 
1665
      end
 
1666
 
 
1667
    end
 
1668
 
 
1669
    # Generate an Image Button Input element as a string.
 
1670
    #
 
1671
    # +src+ is the URL of the image to use for the button.  +name+ 
 
1672
    # is the input name.  +alt+ is the alternative text for the image.
 
1673
    #
 
1674
    # Alternatively, the attributes can be specified as a hash.
 
1675
    # 
 
1676
    #   image_button("url")
 
1677
    #     # <INPUT TYPE="image" SRC="url">
 
1678
    # 
 
1679
    #   image_button("url", "name", "string")
 
1680
    #     # <INPUT TYPE="image" SRC="url" NAME="name" ALT="string">
 
1681
    # 
 
1682
    #   image_button("SRC" => "url", "ATL" => "strng")
 
1683
    #     # <INPUT TYPE="image" SRC="url" ALT="string">
 
1684
    def image_button(src = "", name = nil, alt = nil)
 
1685
      attributes = if src.kind_of?(String)
 
1686
                     { "TYPE" => "image", "SRC" => src, "NAME" => name,
 
1687
                       "ALT" => alt }
 
1688
                   else
 
1689
                     src["TYPE"] = "image"
 
1690
                     src["SRC"] ||= ""
 
1691
                     src
 
1692
                   end
 
1693
      input(attributes)
 
1694
    end
 
1695
 
 
1696
 
 
1697
    # Generate an Image element as a string.
 
1698
    #
 
1699
    # +src+ is the URL of the image.  +alt+ is the alternative text for
 
1700
    # the image.  +width+ is the width of the image, and +height+ is
 
1701
    # its height.
 
1702
    #
 
1703
    # Alternatively, the attributes can be specified as a hash.
 
1704
    #
 
1705
    #   img("src", "alt", 100, 50)
 
1706
    #     # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
 
1707
    # 
 
1708
    #   img("SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50)
 
1709
    #     # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
 
1710
    def img(src = "", alt = "", width = nil, height = nil)
 
1711
      attributes = if src.kind_of?(String)
 
1712
                     { "SRC" => src, "ALT" => alt }
 
1713
                   else
 
1714
                     src
 
1715
                   end
 
1716
      attributes["WIDTH"] = width.to_s if width
 
1717
      attributes["HEIGHT"] = height.to_s if height
 
1718
      super(attributes)
 
1719
    end
 
1720
 
 
1721
 
 
1722
    # Generate a Form element with multipart encoding as a String.
 
1723
    #
 
1724
    # Multipart encoding is used for forms that include file uploads.
 
1725
    #
 
1726
    # +action+ is the action to perform.  +enctype+ is the encoding
 
1727
    # type, which defaults to "multipart/form-data".
 
1728
    #
 
1729
    # Alternatively, the attributes can be specified as a hash.
 
1730
    #
 
1731
    #   multipart_form{ "string" }
 
1732
    #     # <FORM METHOD="post" ENCTYPE="multipart/form-data">string</FORM>
 
1733
    # 
 
1734
    #   multipart_form("url") { "string" }
 
1735
    #     # <FORM METHOD="post" ACTION="url" ENCTYPE="multipart/form-data">string</FORM>
 
1736
    def multipart_form(action = nil, enctype = "multipart/form-data")
 
1737
      attributes = if action == nil
 
1738
                     { "METHOD" => "post", "ENCTYPE" => enctype } 
 
1739
                   elsif action.kind_of?(String)
 
1740
                     { "METHOD" => "post", "ACTION" => action,
 
1741
                       "ENCTYPE" => enctype } 
 
1742
                   else
 
1743
                     unless action.has_key?("METHOD")
 
1744
                       action["METHOD"] = "post"
 
1745
                     end
 
1746
                     unless action.has_key?("ENCTYPE")
 
1747
                       action["ENCTYPE"] = enctype
 
1748
                     end
 
1749
                     action
 
1750
                   end
 
1751
      if block_given?
 
1752
        form(attributes){ yield }
 
1753
      else
 
1754
        form(attributes)
 
1755
      end
 
1756
    end
 
1757
 
 
1758
 
 
1759
    # Generate a Password Input element as a string.
 
1760
    #
 
1761
    # +name+ is the name of the input field.  +value+ is its default
 
1762
    # value.  +size+ is the size of the input field display.  +maxlength+
 
1763
    # is the maximum length of the inputted password.
 
1764
    #
 
1765
    # Alternatively, attributes can be specified as a hash.
 
1766
    #
 
1767
    #   password_field("name")
 
1768
    #     # <INPUT TYPE="password" NAME="name" SIZE="40">
 
1769
    # 
 
1770
    #   password_field("name", "value")
 
1771
    #     # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="40">
 
1772
    # 
 
1773
    #   password_field("password", "value", 80, 200)
 
1774
    #     # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
 
1775
    # 
 
1776
    #   password_field("NAME" => "name", "VALUE" => "value")
 
1777
    #     # <INPUT TYPE="password" NAME="name" VALUE="value">
 
1778
    def password_field(name = "", value = nil, size = 40, maxlength = nil)
 
1779
      attributes = if name.kind_of?(String)
 
1780
                     { "TYPE" => "password", "NAME" => name,
 
1781
                       "VALUE" => value, "SIZE" => size.to_s }
 
1782
                   else
 
1783
                     name["TYPE"] = "password"
 
1784
                     name
 
1785
                   end
 
1786
      attributes["MAXLENGTH"] = maxlength.to_s if maxlength
 
1787
      input(attributes)
 
1788
    end
 
1789
 
 
1790
    # Generate a Select element as a string.
 
1791
    #
 
1792
    # +name+ is the name of the element.  The +values+ are the options that
 
1793
    # can be selected from the Select menu.  Each value can be a String or
 
1794
    # a one, two, or three-element Array.  If a String or a one-element
 
1795
    # Array, this is both the value of that option and the text displayed for
 
1796
    # it.  If a three-element Array, the elements are the option value, displayed
 
1797
    # text, and a boolean value specifying whether this option starts as selected.
 
1798
    # The two-element version omits either the option value (defaults to the same
 
1799
    # as the display text) or the boolean selected specifier (defaults to false).
 
1800
    #
 
1801
    # The attributes and options can also be specified as a hash.  In this
 
1802
    # case, options are specified as an array of values as described above,
 
1803
    # with the hash key of "VALUES".
 
1804
    #
 
1805
    #   popup_menu("name", "foo", "bar", "baz")
 
1806
    #     # <SELECT NAME="name">
 
1807
    #     #   <OPTION VALUE="foo">foo</OPTION>
 
1808
    #     #   <OPTION VALUE="bar">bar</OPTION>
 
1809
    #     #   <OPTION VALUE="baz">baz</OPTION>
 
1810
    #     # </SELECT>
 
1811
    # 
 
1812
    #   popup_menu("name", ["foo"], ["bar", true], "baz")
 
1813
    #     # <SELECT NAME="name">
 
1814
    #     #   <OPTION VALUE="foo">foo</OPTION>
 
1815
    #     #   <OPTION VALUE="bar" SELECTED>bar</OPTION>
 
1816
    #     #   <OPTION VALUE="baz">baz</OPTION>
 
1817
    #     # </SELECT>
 
1818
    # 
 
1819
    #   popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
 
1820
    #     # <SELECT NAME="name">
 
1821
    #     #   <OPTION VALUE="1">Foo</OPTION>
 
1822
    #     #   <OPTION SELECTED VALUE="2">Bar</OPTION>
 
1823
    #     #   <OPTION VALUE="Baz">Baz</OPTION>
 
1824
    #     # </SELECT>
 
1825
    # 
 
1826
    #   popup_menu("NAME" => "name", "SIZE" => 2, "MULTIPLE" => true,
 
1827
    #               "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
 
1828
    #     # <SELECT NAME="name" MULTIPLE SIZE="2">
 
1829
    #     #   <OPTION VALUE="1">Foo</OPTION>
 
1830
    #     #   <OPTION SELECTED VALUE="2">Bar</OPTION>
 
1831
    #     #   <OPTION VALUE="Baz">Baz</OPTION>
 
1832
    #     # </SELECT>
 
1833
    def popup_menu(name = "", *values)
 
1834
 
 
1835
      if name.kind_of?(Hash)
 
1836
        values   = name["VALUES"]
 
1837
        size     = name["SIZE"].to_s if name["SIZE"]
 
1838
        multiple = name["MULTIPLE"]
 
1839
        name     = name["NAME"]
 
1840
      else
 
1841
        size = nil
 
1842
        multiple = nil
 
1843
      end
 
1844
 
 
1845
      select({ "NAME" => name, "SIZE" => size,
 
1846
               "MULTIPLE" => multiple }){
 
1847
        values.collect{|value|
 
1848
          if value.kind_of?(String)
 
1849
            option({ "VALUE" => value }){ value }
 
1850
          else
 
1851
            if value[value.size - 1] == true
 
1852
              option({ "VALUE" => value[0], "SELECTED" => true }){
 
1853
                value[value.size - 2]
 
1854
              }
 
1855
            else
 
1856
              option({ "VALUE" => value[0] }){
 
1857
                value[value.size - 1]
 
1858
              }
 
1859
            end
 
1860
          end
 
1861
        }.to_s
 
1862
      }
 
1863
 
 
1864
    end
 
1865
 
 
1866
    # Generates a radio-button Input element.
 
1867
    #
 
1868
    # +name+ is the name of the input field.  +value+ is the value of
 
1869
    # the field if checked.  +checked+ specifies whether the field
 
1870
    # starts off checked.
 
1871
    #
 
1872
    # Alternatively, the attributes can be specified as a hash.
 
1873
    #
 
1874
    #   radio_button("name", "value")
 
1875
    #     # <INPUT TYPE="radio" NAME="name" VALUE="value">
 
1876
    # 
 
1877
    #   radio_button("name", "value", true)
 
1878
    #     # <INPUT TYPE="radio" NAME="name" VALUE="value" CHECKED>
 
1879
    # 
 
1880
    #   radio_button("NAME" => "name", "VALUE" => "value", "ID" => "foo")
 
1881
    #     # <INPUT TYPE="radio" NAME="name" VALUE="value" ID="foo">
 
1882
    def radio_button(name = "", value = nil, checked = nil)
 
1883
      attributes = if name.kind_of?(String)
 
1884
                     { "TYPE" => "radio", "NAME" => name,
 
1885
                       "VALUE" => value, "CHECKED" => checked }
 
1886
                   else
 
1887
                     name["TYPE"] = "radio"
 
1888
                     name
 
1889
                   end
 
1890
      input(attributes)
 
1891
    end
 
1892
 
 
1893
    # Generate a sequence of radio button Input elements, as a String.
 
1894
    #
 
1895
    # This works the same as #checkbox_group().  However, it is not valid
 
1896
    # to have more than one radiobutton in a group checked.
 
1897
    # 
 
1898
    #   radio_group("name", "foo", "bar", "baz")
 
1899
    #     # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
 
1900
    #     # <INPUT TYPE="radio" NAME="name" VALUE="bar">bar
 
1901
    #     # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
 
1902
    # 
 
1903
    #   radio_group("name", ["foo"], ["bar", true], "baz")
 
1904
    #     # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
 
1905
    #     # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="bar">bar
 
1906
    #     # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
 
1907
    # 
 
1908
    #   radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
 
1909
    #     # <INPUT TYPE="radio" NAME="name" VALUE="1">Foo
 
1910
    #     # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="2">Bar
 
1911
    #     # <INPUT TYPE="radio" NAME="name" VALUE="Baz">Baz
 
1912
    # 
 
1913
    #   radio_group("NAME" => "name",
 
1914
    #                 "VALUES" => ["foo", "bar", "baz"])
 
1915
    # 
 
1916
    #   radio_group("NAME" => "name",
 
1917
    #                 "VALUES" => [["foo"], ["bar", true], "baz"])
 
1918
    # 
 
1919
    #   radio_group("NAME" => "name",
 
1920
    #                 "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
 
1921
    def radio_group(name = "", *values)
 
1922
      if name.kind_of?(Hash)
 
1923
        values = name["VALUES"]
 
1924
        name = name["NAME"]
 
1925
      end
 
1926
      values.collect{|value|
 
1927
        if value.kind_of?(String)
 
1928
          radio_button(name, value) + value
 
1929
        else
 
1930
          if value[value.size - 1] == true
 
1931
            radio_button(name, value[0], true) +
 
1932
            value[value.size - 2]
 
1933
          else
 
1934
            radio_button(name, value[0]) +
 
1935
            value[value.size - 1]
 
1936
          end
 
1937
        end
 
1938
      }.to_s
 
1939
    end
 
1940
 
 
1941
    # Generate a reset button Input element, as a String.
 
1942
    #
 
1943
    # This resets the values on a form to their initial values.  +value+
 
1944
    # is the text displayed on the button. +name+ is the name of this button.
 
1945
    #
 
1946
    # Alternatively, the attributes can be specified as a hash.
 
1947
    #
 
1948
    #   reset
 
1949
    #     # <INPUT TYPE="reset">
 
1950
    # 
 
1951
    #   reset("reset")
 
1952
    #     # <INPUT TYPE="reset" VALUE="reset">
 
1953
    # 
 
1954
    #   reset("VALUE" => "reset", "ID" => "foo")
 
1955
    #     # <INPUT TYPE="reset" VALUE="reset" ID="foo">
 
1956
    def reset(value = nil, name = nil)
 
1957
      attributes = if (not value) or value.kind_of?(String)
 
1958
                     { "TYPE" => "reset", "VALUE" => value, "NAME" => name }
 
1959
                   else
 
1960
                     value["TYPE"] = "reset"
 
1961
                     value
 
1962
                   end
 
1963
      input(attributes)
 
1964
    end
 
1965
 
 
1966
    alias scrolling_list popup_menu
 
1967
 
 
1968
    # Generate a submit button Input element, as a String.
 
1969
    #
 
1970
    # +value+ is the text to display on the button.  +name+ is the name
 
1971
    # of the input.
 
1972
    #
 
1973
    # Alternatively, the attributes can be specified as a hash.
 
1974
    #
 
1975
    #   submit
 
1976
    #     # <INPUT TYPE="submit">
 
1977
    # 
 
1978
    #   submit("ok")
 
1979
    #     # <INPUT TYPE="submit" VALUE="ok">
 
1980
    # 
 
1981
    #   submit("ok", "button1")
 
1982
    #     # <INPUT TYPE="submit" VALUE="ok" NAME="button1">
 
1983
    # 
 
1984
    #   submit("VALUE" => "ok", "NAME" => "button1", "ID" => "foo")
 
1985
    #     # <INPUT TYPE="submit" VALUE="ok" NAME="button1" ID="foo">
 
1986
    def submit(value = nil, name = nil)
 
1987
      attributes = if (not value) or value.kind_of?(String)
 
1988
                     { "TYPE" => "submit", "VALUE" => value, "NAME" => name }
 
1989
                   else
 
1990
                     value["TYPE"] = "submit"
 
1991
                     value
 
1992
                   end
 
1993
      input(attributes)
 
1994
    end
 
1995
 
 
1996
    # Generate a text field Input element, as a String.
 
1997
    #
 
1998
    # +name+ is the name of the input field.  +value+ is its initial
 
1999
    # value.  +size+ is the size of the input area.  +maxlength+
 
2000
    # is the maximum length of input accepted.
 
2001
    #
 
2002
    # Alternatively, the attributes can be specified as a hash.
 
2003
    #
 
2004
    #   text_field("name")
 
2005
    #     # <INPUT TYPE="text" NAME="name" SIZE="40">
 
2006
    # 
 
2007
    #   text_field("name", "value")
 
2008
    #     # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="40">
 
2009
    # 
 
2010
    #   text_field("name", "value", 80)
 
2011
    #     # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80">
 
2012
    # 
 
2013
    #   text_field("name", "value", 80, 200)
 
2014
    #     # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
 
2015
    # 
 
2016
    #   text_field("NAME" => "name", "VALUE" => "value")
 
2017
    #     # <INPUT TYPE="text" NAME="name" VALUE="value">
 
2018
    def text_field(name = "", value = nil, size = 40, maxlength = nil)
 
2019
      attributes = if name.kind_of?(String)
 
2020
                     { "TYPE" => "text", "NAME" => name, "VALUE" => value,
 
2021
                       "SIZE" => size.to_s }
 
2022
                   else
 
2023
                     name["TYPE"] = "text"
 
2024
                     name
 
2025
                   end
 
2026
      attributes["MAXLENGTH"] = maxlength.to_s if maxlength
 
2027
      input(attributes)
 
2028
    end
 
2029
 
 
2030
    # Generate a TextArea element, as a String.
 
2031
    #
 
2032
    # +name+ is the name of the textarea.  +cols+ is the number of
 
2033
    # columns and +rows+ is the number of rows in the display.
 
2034
    #
 
2035
    # Alternatively, the attributes can be specified as a hash.
 
2036
    #
 
2037
    # The body is provided by the passed-in no-argument block
 
2038
    #
 
2039
    #   textarea("name")
 
2040
    #      # = textarea("NAME" => "name", "COLS" => 70, "ROWS" => 10)
 
2041
    #
 
2042
    #   textarea("name", 40, 5)
 
2043
    #      # = textarea("NAME" => "name", "COLS" => 40, "ROWS" => 5)
 
2044
    def textarea(name = "", cols = 70, rows = 10)  # :yield:
 
2045
      attributes = if name.kind_of?(String)
 
2046
                     { "NAME" => name, "COLS" => cols.to_s,
 
2047
                       "ROWS" => rows.to_s }
 
2048
                   else
 
2049
                     name
 
2050
                   end
 
2051
      if block_given?
 
2052
        super(attributes){ yield }
 
2053
      else
 
2054
        super(attributes)
 
2055
      end
 
2056
    end
 
2057
 
 
2058
  end # HtmlExtension
 
2059
 
 
2060
 
 
2061
  # Mixin module for HTML version 3 generation methods.
 
2062
  module Html3 # :nodoc:
 
2063
 
 
2064
    # The DOCTYPE declaration for this version of HTML
 
2065
    def doctype
 
2066
      %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">|
 
2067
    end
 
2068
 
 
2069
    # Initialise the HTML generation methods for this version.
 
2070
    def element_init
 
2071
      extend TagMaker
 
2072
      methods = ""
 
2073
      # - -
 
2074
      for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG
 
2075
          DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV center MAP
 
2076
          APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT table TITLE
 
2077
          STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE
 
2078
          CAPTION ]
 
2079
        methods += <<-BEGIN + nn_element_def(element) + <<-END
 
2080
          def #{element.downcase}(attributes = {})
 
2081
        BEGIN
 
2082
          end
 
2083
        END
 
2084
      end
 
2085
 
 
2086
      # - O EMPTY
 
2087
      for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
 
2088
          ISINDEX META ]
 
2089
        methods += <<-BEGIN + nOE_element_def(element) + <<-END
 
2090
          def #{element.downcase}(attributes = {})
 
2091
        BEGIN
 
2092
          end
 
2093
        END
 
2094
      end
 
2095
 
 
2096
      # O O or - O
 
2097
      for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr
 
2098
          th td ]
 
2099
        methods += <<-BEGIN + nO_element_def(element) + <<-END
 
2100
          def #{element.downcase}(attributes = {})
 
2101
        BEGIN
 
2102
          end
 
2103
        END
 
2104
      end
 
2105
      eval(methods)
 
2106
    end
 
2107
 
 
2108
  end # Html3
 
2109
 
 
2110
 
 
2111
  # Mixin module for HTML version 4 generation methods.
 
2112
  module Html4 # :nodoc:
 
2113
 
 
2114
    # The DOCTYPE declaration for this version of HTML
 
2115
    def doctype
 
2116
      %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|
 
2117
    end
 
2118
 
 
2119
    # Initialise the HTML generation methods for this version.
 
2120
    def element_init
 
2121
      extend TagMaker
 
2122
      methods = ""
 
2123
      # - -
 
2124
      for element in %w[ TT I B BIG SMALL EM STRONG DFN CODE SAMP KBD
 
2125
        VAR CITE ABBR ACRONYM SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT
 
2126
        H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT OPTGROUP
 
2127
        FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT
 
2128
        TEXTAREA FORM A BLOCKQUOTE CAPTION ]
 
2129
        methods += <<-BEGIN + nn_element_def(element) + <<-END
 
2130
          def #{element.downcase}(attributes = {})
 
2131
        BEGIN
 
2132
          end
 
2133
        END
 
2134
      end
 
2135
 
 
2136
      # - O EMPTY
 
2137
      for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META ]
 
2138
        methods += <<-BEGIN + nOE_element_def(element) + <<-END
 
2139
          def #{element.downcase}(attributes = {})
 
2140
        BEGIN
 
2141
          end
 
2142
        END
 
2143
      end
 
2144
 
 
2145
      # O O or - O
 
2146
      for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
 
2147
          COLGROUP TR TH TD HEAD]
 
2148
        methods += <<-BEGIN + nO_element_def(element) + <<-END
 
2149
          def #{element.downcase}(attributes = {})
 
2150
        BEGIN
 
2151
          end
 
2152
        END
 
2153
      end
 
2154
      eval(methods)
 
2155
    end
 
2156
 
 
2157
  end # Html4
 
2158
 
 
2159
 
 
2160
  # Mixin module for HTML version 4 transitional generation methods.
 
2161
  module Html4Tr # :nodoc:
 
2162
 
 
2163
    # The DOCTYPE declaration for this version of HTML
 
2164
    def doctype
 
2165
      %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|
 
2166
    end
 
2167
 
 
2168
    # Initialise the HTML generation methods for this version.
 
2169
    def element_init
 
2170
      extend TagMaker
 
2171
      methods = ""
 
2172
      # - -
 
2173
      for element in %w[ TT I B U S STRIKE BIG SMALL EM STRONG DFN
 
2174
          CODE SAMP KBD VAR CITE ABBR ACRONYM FONT SUB SUP SPAN BDO
 
2175
          ADDRESS DIV CENTER MAP OBJECT APPLET H1 H2 H3 H4 H5 H6 PRE Q
 
2176
          INS DEL DL OL UL DIR MENU LABEL SELECT OPTGROUP FIELDSET
 
2177
          LEGEND BUTTON TABLE IFRAME NOFRAMES TITLE STYLE SCRIPT
 
2178
          NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ]
 
2179
        methods += <<-BEGIN + nn_element_def(element) + <<-END
 
2180
          def #{element.downcase}(attributes = {})
 
2181
        BEGIN
 
2182
          end
 
2183
        END
 
2184
      end
 
2185
 
 
2186
      # - O EMPTY
 
2187
      for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
 
2188
          COL ISINDEX META ]
 
2189
        methods += <<-BEGIN + nOE_element_def(element) + <<-END
 
2190
          def #{element.downcase}(attributes = {})
 
2191
        BEGIN
 
2192
          end
 
2193
        END
 
2194
      end
 
2195
 
 
2196
      # O O or - O
 
2197
      for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
 
2198
          COLGROUP TR TH TD HEAD ]
 
2199
        methods += <<-BEGIN + nO_element_def(element) + <<-END
 
2200
          def #{element.downcase}(attributes = {})
 
2201
        BEGIN
 
2202
          end
 
2203
        END
 
2204
      end
 
2205
      eval(methods)
 
2206
    end
 
2207
 
 
2208
  end # Html4Tr
 
2209
 
 
2210
 
 
2211
  # Mixin module for generating HTML version 4 with framesets.
 
2212
  module Html4Fr # :nodoc:
 
2213
 
 
2214
    # The DOCTYPE declaration for this version of HTML
 
2215
    def doctype
 
2216
      %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|
 
2217
    end
 
2218
 
 
2219
    # Initialise the HTML generation methods for this version.
 
2220
    def element_init
 
2221
      methods = ""
 
2222
      # - -
 
2223
      for element in %w[ FRAMESET ]
 
2224
        methods += <<-BEGIN + nn_element_def(element) + <<-END
 
2225
          def #{element.downcase}(attributes = {})
 
2226
        BEGIN
 
2227
          end
 
2228
        END
 
2229
      end
 
2230
 
 
2231
      # - O EMPTY
 
2232
      for element in %w[ FRAME ]
 
2233
        methods += <<-BEGIN + nOE_element_def(element) + <<-END
 
2234
          def #{element.downcase}(attributes = {})
 
2235
        BEGIN
 
2236
          end
 
2237
        END
 
2238
      end
 
2239
      eval(methods)
 
2240
    end
 
2241
 
 
2242
  end # Html4Fr
 
2243
 
 
2244
 
 
2245
  # Creates a new CGI instance.
 
2246
  #
 
2247
  # +type+ specifies which version of HTML to load the HTML generation
 
2248
  # methods for.  The following versions of HTML are supported:
 
2249
  #
 
2250
  # html3:: HTML 3.x
 
2251
  # html4:: HTML 4.0
 
2252
  # html4Tr:: HTML 4.0 Transitional
 
2253
  # html4Fr:: HTML 4.0 with Framesets
 
2254
  #
 
2255
  # If not specified, no HTML generation methods will be loaded.
 
2256
  #
 
2257
  # If the CGI object is not created in a standard CGI call environment
 
2258
  # (that is, it can't locate REQUEST_METHOD in its environment), then
 
2259
  # it will run in "offline" mode.  In this mode, it reads its parameters
 
2260
  # from the command line or (failing that) from standard input.  Otherwise,
 
2261
  # cookies and other parameters are parsed automatically from the standard
 
2262
  # CGI locations, which varies according to the REQUEST_METHOD.
 
2263
  def initialize(type = "query")
 
2264
    if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
 
2265
      Apache.request.setup_cgi_env
 
2266
    end
 
2267
 
 
2268
    extend QueryExtension
 
2269
    @multipart = false
 
2270
    if defined?(CGI_PARAMS)
 
2271
      warn "do not use CGI_PARAMS and CGI_COOKIES"
 
2272
      @params = CGI_PARAMS.dup
 
2273
      @cookies = CGI_COOKIES.dup
 
2274
    else
 
2275
      initialize_query()  # set @params, @cookies
 
2276
    end
 
2277
    @output_cookies = nil
 
2278
    @output_hidden = nil
 
2279
 
 
2280
    case type
 
2281
    when "html3"
 
2282
      extend Html3
 
2283
      element_init()
 
2284
      extend HtmlExtension
 
2285
    when "html4"
 
2286
      extend Html4
 
2287
      element_init()
 
2288
      extend HtmlExtension
 
2289
    when "html4Tr"
 
2290
      extend Html4Tr
 
2291
      element_init()
 
2292
      extend HtmlExtension
 
2293
    when "html4Fr"
 
2294
      extend Html4Tr
 
2295
      element_init()
 
2296
      extend Html4Fr
 
2297
      element_init()
 
2298
      extend HtmlExtension
 
2299
    end
 
2300
  end
 
2301
 
 
2302
end   # class CGI