00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 package require Tcl 8.2
00017 package require ncgi
00018 package provide html 1.4
00019
00020 namespace ::html {
00021
00022
00023
00024 variable page
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 variable defaults
00038 array defaults = {
00039 input.size 45
00040 body.bgcolor white
00041 body.text black
00042 }
00043
00044
00045
00046
00047
00048
00049 variable randVar 0
00050
00051
00052
00053
00054
00055
00056
00057 variable entities {
00058 \xa0 \xa1 ¡ \xa2 ¢ \xa3 £ \xa4 ¤
00059 \xa5 ¥ \xa6 ¦ \xa7 § \xa8 ¨ \xa9 ©
00060 \xaa ª \xab « \xac ¬ \xad ­ \xae ®
00061 \xaf ¯ \xb0 ° \xb1 ± \xb2 ² \xb3 ³
00062 \xb4 ´ \xb5 µ \xb6 ¶ \xb7 · \xb8 ¸
00063 \xb9 ¹ \xba º \xbb » \xbc ¼ \xbd ½
00064 \xbe ¾ \xbf ¿ \xc0 À \xc1 Á \xc2 Â
00065 \xc3 Ã \xc4 Ä \xc5 Å \xc6 Æ \xc7 Ç
00066 \xc8 È \xc9 É \xca Ê \xcb Ë \xcc Ì
00067 \xcd Í \xce Î \xcf Ï \xd0 Ð \xd1 Ñ
00068 \xd2 Ò \xd3 Ó \xd4 Ô \xd5 Õ \xd6 Ö
00069 \xd7 × \xd8 Ø \xd9 Ù \xda Ú \xdb Û
00070 \xdc Ü \xdd Ý \xde Þ \xdf ß \xe0 à
00071 \xe1 á \xe2 â \xe3 ã \xe4 ä \xe5 å
00072 \xe6 æ \xe7 ç \xe8 è \xe9 é \xea ê
00073 \xeb ë \xec ì \xed í \xee î \xef ï
00074 \xf0 ð \xf1 ñ \xf2 ò \xf3 ó \xf4 ô
00075 \xf5 õ \xf6 ö \xf7 ÷ \xf8 ø \xf9 ù
00076 \xfa ú \xfb û \xfc ü \xfd ý \xfe þ
00077 \xff ÿ \u192 ƒ \u391 Α \u392 Β \u393 Γ
00078 \u394 Δ \u395 Ε \u396 Ζ \u397 Η \u398 Θ
00079 \u399 Ι \u39A Κ \u39B Λ \u39C Μ \u39D Ν
00080 \u39E Ξ \u39F Ο \u3A0 Π \u3A1 Ρ \u3A3 Σ
00081 \u3A4 Τ \u3A5 Υ \u3A6 Φ \u3A7 Χ \u3A8 Ψ
00082 \u3A9 Ω \u3B1 α \u3B2 β \u3B3 γ \u3B4 δ
00083 \u3B5 ε \u3B6 ζ \u3B7 η \u3B8 θ \u3B9 ι
00084 \u3BA κ \u3BB λ \u3BC μ \u3BD ν \u3BE ξ
00085 \u3BF ο \u3C0 π \u3C1 ρ \u3C2 ς \u3C3 σ
00086 \u3C4 τ \u3C5 υ \u3C6 φ \u3C7 χ \u3C8 ψ
00087 \u3C9 ω \u3D1 ϑ \u3D2 ϒ \u3D6 ϖ
00088 \u2022 • \u2026 … \u2032 ′ \u2033 ″
00089 \u203E ‾ \u2044 ⁄ \u2118 ℘ \u2111 ℑ
00090 \u211C ℜ \u2122 ™ \u2135 ℵ \u2190 ←
00091 \u2191 ↑ \u2192 → \u2193 ↓ \u2194 ↔ \u21B5 ↵
00092 \u21D0 ⇐ \u21D1 ⇑ \u21D2 ⇒ \u21D3 ⇓ \u21D4 ⇔
00093 \u2200 ∀ \u2202 ∂ \u2203 ∃ \u2205 ∅
00094 \u2207 ∇ \u2208 ∈ \u2209 ∉ \u220B ∋ \u220F ∏
00095 \u2211 ∑ \u2212 − \u2217 ∗ \u221A √
00096 \u221D ∝ \u221E ∞ \u2220 ∠ \u2227 ∧ \u2228 ∨
00097 \u2229 ∩ \u222A ∪ \u222B ∫ \u2234 ∴ \u223C ∼
00098 \u2245 ≅ \u2248 ≈ \u2260 ≠ \u2261 ≡ \u2264 ≤
00099 \u2265 ≥ \u2282 ⊂ \u2283 ⊃ \u2284 ⊄ \u2286 ⊆
00100 \u2287 ⊇ \u2295 ⊕ \u2297 ⊗ \u22A5 ⊥
00101 \u22C5 ⋅ \u2308 ⌈ \u2309 ⌉ \u230A ⌊
00102 \u230B ⌋ \u2329 ⟨ \u232A ⟩ \u25CA ◊
00103 \u2660 ♠ \u2663 ♣ \u2665 ♥ \u2666 ♦
00104 \x22 " \x26 & \x3C < \x3E > \u152 Œ
00105 \u153 œ \u160 Š \u161 š \u178 Ÿ
00106 \u2C6 ˆ \u2DC ˜ \u2002   \u2003   \u2009  
00107 \u200C ‌ \u200D ‍ \u200E ‎ \u200F ‏ \u2013 –
00108 \u2014 — \u2018 ‘ \u2019 ’ \u201A ‚
00109 \u201C “ \u201D ” \u201E „ \u2020 †
00110 \u2021 ‡ \u2030 ‰ \u2039 ‹ \u203A ›
00111 \u20AC €
00112 }
00113 }
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135 ret ::html::foreach (type vars , type vals , type args) {
00136 variable randVar
00137
00138 # The body of the foreach loop must be run in the stack frame
00139 # above this one in order to have access to local variable at that stack
00140 # level.
00141
00142 # To support nested foreach loops, we use a uniquely named
00143 # variable to store incremental results.
00144 incr randVar
00145 ::set resultVar "result_$randVar"
00146
00147 # Extract the body and any varlists and valuelists from the args.
00148 ::set body [lindex $args end]
00149 ::set varvals [linsert [lreplace $args end end] 0 $vars $vals]
00150
00151 # Create the script to eval in the stack frame above this one.
00152 ::set script "::foreach"
00153 ::foreach {vars vals} $varvals {
00154 append script " [list $vars] [list $vals]"
00155 }
00156 append script " \{\n"
00157 append script " append $resultVar \[subst \{$body\}\]\n"
00158 append script "\}\n"
00159
00160 # Create a temporary variable in the stack frame above this one,
00161 # and use it to store the incremental results of the multiple loop
00162 # iterations. Remove the temporary variable when we're done so there's
00163 # no trace of this loop left in that stack frame.
00164
00165 upvar 1 $resultVar tmp
00166 ::set tmp ""
00167 uplevel 1 $script
00168 ::set result $tmp
00169 unset tmp
00170 return $result
00171 }
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194 ret ::html::for (type start , type test , type next , type body) {
00195 variable randVar
00196
00197 # The body of the for loop must be run in the stack frame
00198 # above this one in order to have access to local variable at that stack
00199 # level.
00200
00201 # To support nested for loops, we use a uniquely named
00202 # variable to store incremental results.
00203 incr randVar
00204 ::set resultVar "result_$randVar"
00205
00206 # Create the script to eval in the stack frame above this one.
00207 ::set script "::for [list $start] [list $test] [list $next] \{\n"
00208 append script " append $resultVar \[subst \{$body\}\]\n"
00209 append script "\}\n"
00210
00211 # Create a temporary variable in the stack frame above this one,
00212 # and use it to store the incremental resutls of the multiple loop
00213 # iterations. Remove the temporary variable when we're done so there's
00214 # no trace of this loop left in that stack frame.
00215
00216 upvar 1 $resultVar tmp
00217 ::set tmp ""
00218 uplevel 1 $script
00219 ::set result $tmp
00220 unset tmp
00221 return $result
00222 }
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243 ret ::html::while (type test , type body) {
00244 variable randVar
00245
00246 # The body of the while loop must be run in the stack frame
00247 # above this one in order to have access to local variable at that stack
00248 # level.
00249
00250 # To support nested while loops, we use a uniquely named
00251 # variable to store incremental results.
00252 incr randVar
00253 ::set resultVar "result_$randVar"
00254
00255 # Create the script to eval in the stack frame above this one.
00256 ::set script "::while [list $test] \{\n"
00257 append script " append $resultVar \[subst \{$body\}\]\n"
00258 append script "\}\n"
00259
00260 # Create a temporary variable in the stack frame above this one,
00261 # and use it to store the incremental resutls of the multiple loop
00262 # iterations. Remove the temporary variable when we're done so there's
00263 # no trace of this loop left in that stack frame.
00264
00265 upvar 1 $resultVar tmp
00266 ::set tmp ""
00267 uplevel 1 $script
00268 ::set result $tmp
00269 unset tmp
00270 return $result
00271 }
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291 ret ::html::if (type test , type body , type args) {
00292 variable randVar
00293
00294 # The body of the then/else clause must be run in the stack frame
00295 # above this one in order to have access to local variable at that stack
00296 # level.
00297
00298 # To support nested if's, we use a uniquely named
00299 # variable to store incremental results.
00300 incr randVar
00301 ::set resultVar "result_$randVar"
00302
00303 # Extract the elseif clauses and else clause if they exist.
00304 ::set cmd [linsert $args 0 "::if" $test $body]
00305
00306 ::foreach {keyword test body} $cmd {
00307 ::if {[string equal $keyword "else"]} {
00308 append script " else \{\n"
00309 ::set body $test
00310 } else {
00311 append script " $keyword [list $test] \{\n"
00312 }
00313 append script " append $resultVar \[subst \{$body\}\]\n"
00314 append script "\} "
00315 }
00316
00317 # Create a temporary variable in the stack frame above this one,
00318 # and use it to store the incremental resutls of the multiple loop
00319 # iterations. Remove the temporary variable when we're done so there's
00320 # no trace of this loop left in that stack frame.
00321
00322 upvar $resultVar tmp
00323 ::set tmp ""
00324 uplevel $script
00325 ::set result $tmp
00326 unset tmp
00327 return $result
00328 }
00329
00330 # ::html::set
00331 #
00332 # Rework the "set" command to blend into HTML template files.
00333 # The return value is always "" so nothing is appended in the
00334 # template. No error checking is done on any arguments.
00335 #
00336 # Arguments:
00337 # var The variable to set.
00338 # val The new value to give the variable.
00339 #
00340 # Results:
00341 # Returns "".
00342 #
00343 # Side Effects:
00344 # None.
00345
00346 proc ::html::set {var val} {
00347
00348 # The variable must be set in the stack frame above this one.
00349
00350 ::set cmd [list set $var $val]
00351 uplevel 1 $cmd
00352 return ""
00353 }
00354
00355 # ::html::eval
00356 #
00357 # Rework the "eval" command to blend into HTML template files.
00358 # The return value is always "" so nothing is appended in the
00359 # template. No error checking is done on any arguments.
00360 #
00361 # Arguments:
00362 # args The args to evaluate. At least one must be given.
00363 #
00364 # Results:
00365 # Returns "".
00366 #
00367 # Side Effects:
00368 # Throws an error if no arguments are given.
00369
00370 proc ::html::eval {args} {
00371
00372 # The args must be evaluated in the stack frame above this one.
00373 ::eval [linsert $args 0 uplevel 1]
00374 return ""
00375 }
00376
00377 # ::html::init
00378 #
00379 # Reset state that gets accumulated for the current page.
00380 #
00381 # Arguments:
00382 # nvlist Name, value list that is used to initialize default namespace
00383 # variables that set font, size, etc.
00384 #
00385 # Side Effects:
00386 # Wipes the page state array
00387
00388 proc ::html::init {{nvlist {}}} {
00389 variable page
00390 variable defaults
00391 ::if {[info exists page]} {
00392 unset page
00393 }
00394 ::if {[info exists defaults]} {
00395 unset defaults
00396 }
00397 array set defaults $nvlist
00398 }
00399
00400 # ::html::head
00401 #
00402 # Generate the <head> section. There are a number of
00403 # optional calls you make *before* this to inject
00404 # meta tags - see everything between here and the bodyTag proc.
00405 #
00406 # Arguments:
00407 # title The page title
00408 #
00409 # Results:
00410 # HTML for the <head> section
00411
00412 proc ::html::head {title} {
00413 variable page
00414 ::set html "[openTag html][openTag head]\n"
00415 append html "\t[title $title]"
00416 ::if {[info exists page(author)]} {
00417 append html "\t$page(author)"
00418 }
00419 ::if {[info exists page(meta)]} {
00420 ::foreach line $page(meta) {
00421 append html "\t$line\n"
00422 }
00423 }
00424 ::if {[info exists page(css)]} {
00425 ::foreach style $page(css) {
00426 append html "\t$style\n"
00427 }
00428 }
00429 ::if {[info exists page(js)]} {
00430 ::foreach script $page(js) {
00431 append html "\t$script\n"
00432 }
00433 }
00434 append html "[closeTag]\n"
00435 }
00436
00437 # ::html::title
00438 #
00439 # Wrap up the <title> and tuck it away for use in the page later.
00440 #
00441 # Arguments:
00442 # title The page title
00443 #
00444 # Results:
00445 # HTML for the <title> section
00446
00447 proc ::html::title {title} {
00448 variable page
00449 ::set page(title) $title
00450 ::set html "<title>$title</title>\n"
00451 return $html
00452 }
00453
00454 # ::html::getTitle
00455 #
00456 # Return the title of the current page.
00457 #
00458 # Arguments:
00459 # None
00460 #
00461 # Results:
00462 # The title
00463
00464 proc ::html::getTitle {} {
00465 variable page
00466 ::if {[info exists page(title)]} {
00467 return $page(title)
00468 } else {
00469 return ""
00470 }
00471 }
00472
00473 # ::html::meta
00474 #
00475 # Generate a meta tag. This tag gets bundled into the <head>
00476 # section generated by html::head
00477 #
00478 # Arguments:
00479 # args A name-value list of meta tag names and values.
00480 #
00481 # Side Effects:
00482 # Stores HTML for the <meta> tag for use later by html::head
00483
00484 proc ::html::meta {args} {
00485 variable page
00486 ::set html ""
00487 ::foreach {name value} $args {
00488 append html "<meta name=\"$name\" content=\"[quoteFormValue $value]\">"
00489 }
00490 lappend page(meta) $html
00491 return ""
00492 }
00493
00494 # ::html::refresh
00495 #
00496 # Generate a meta refresh tag. This tag gets bundled into the <head>
00497 # section generated by html::head
00498 #
00499 # Arguments:
00500 # content Time period, in seconds, before the refresh
00501 # url (option) new page to view. If not specified, then
00502 # the current page is reloaded.
00503 #
00504 # Side Effects:
00505 # Stores HTML for the <meta> tag for use later by html::head
00506
00507 proc ::html::refresh {content {url {}}} {
00508 variable page
00509 ::set html "<meta http-equiv=\"Refresh\" content=\"$content"
00510 ::if {[string length $url]} {
00511 append html "; url=$url"
00512 }
00513 append html "\">\n"
00514 lappend page(meta) $html
00515 return ""
00516 }
00517
00518 # ::html::headTag
00519 #
00520 # Embed a tag into the HEAD section
00521 # generated by html::head
00522 #
00523 # Arguments:
00524 # string Everything but the < > for the tag.
00525 #
00526 # Side Effects:
00527 # Stores HTML for the tag for use later by html::head
00528
00529 proc ::html::headTag {string} {
00530 variable page
00531 lappend page(meta) <$string>
00532 return ""
00533 }
00534
00535 # ::html::keywords
00536 #
00537 # Add META tag keywords to the <head> section.
00538 # Call this before you call html::head
00539 #
00540 # Arguments:
00541 # args The keywords
00542 #
00543 # Side Effects:
00544 # See html::meta
00545
00546 proc ::html::keywords {args} {
00547 html::meta keywords [join $args ", "]
00548 }
00549
00550 # ::html::description
00551 #
00552 # Add a description META tag to the <head> section.
00553 # Call this before you call html::head
00554 #
00555 # Arguments:
00556 # description The description
00557 #
00558 # Side Effects:
00559 # See html::meta
00560
00561 proc ::html::description {description} {
00562 html::meta description $description
00563 }
00564
00565 # ::html::author
00566 #
00567 # Add an author comment to the <head> section.
00568 # Call this before you call html::head
00569 #
00570 # Arguments:
00571 # author Author's name
00572 #
00573 # Side Effects:
00574 # sets page(author)
00575
00576 proc ::html::author {author} {
00577 variable page
00578 ::set page(author) "<!-- $author -->\n"
00579 return ""
00580 }
00581
00582 # ::html::tagParam
00583 #
00584 # Return a name, value string for the tag parameters.
00585 # The values come from "hard-wired" values in the
00586 # param agrument, or from the defaults set with html::init.
00587 #
00588 # Arguments:
00589 # tag Name of the HTML tag (case insensitive).
00590 # param pname=value info that overrides any default values
00591 #
00592 # Results
00593 # A string of the form:
00594 # pname="keyvalue" name2="2nd value"
00595
00596 proc ::html::tagParam {tag {param {}}} {
00597 variable defaults
00598
00599 ::set def ""
00600 ::foreach key [lsort [array names defaults $tag.*]] {
00601 append def [default $key $param]
00602 }
00603 return [string trimleft $param$def]
00604 }
00605
00606 # ::html::default
00607 #
00608 # Return a default value, if one has been registered
00609 # and an overriding value does not occur in the existing
00610 # tag parameters.
00611 #
00612 # Arguments:
00613 # key Index into the defaults array defined by html::init
00614 # This is expected to be in the form tag.pname where
00615 # the pname part is used in the tag parameter name
00616 # param pname=value info that overrides any default values
00617 #
00618 # Results
00619 # pname="keyvalue"
00620
00621 proc ::html::default {key {param {}}} {
00622 variable defaults
00623 ::set pname [string tolower [lindex [split $key .] 1]]
00624 ::set key [string tolower $key]
00625 ::if {![regexp -nocase "(\[ \]|^)$pname=" $param] &&
00626 [info exists defaults($key)] &&
00627 [string length $defaults($key)]} {
00628 return " $pname=\"$defaults($key)\""
00629 } else {
00630 return ""
00631 }
00632 }
00633
00634 # ::html::bodyTag
00635 #
00636 # Generate a body tag
00637 #
00638 # Arguments:
00639 # none
00640 #
00641 # Results
00642 # A body tag
00643
00644 proc ::html::bodyTag {args} {
00645 return [openTag body [join $args]]\n
00646 }
00647
00648 # The following procedures are all related to generating form elements
00649 # that are initialized to store the current value of the form element
00650 # based on the CGI state. These functions depend on the ncgi::value
00651 # procedure and assume that the caller has called ncgi::parse and/or
00652 # ncgi::init appropriately to initialize the ncgi module.
00653
00654 # ::html::formValue
00655 #
00656 # Return a name and value pair, where the value is initialized
00657 # from existing form data, if any.
00658 #
00659 # Arguments:
00660 # name The name of the form element
00661 # defvalue A default value to use, if not appears in the CGI
00662 # inputs. DEPRECATED - use ncgi::defValue instead.
00663 #
00664 # Retults:
00665 # A string like:
00666 # name="fred" value="freds value"
00667
00668 proc ::html::formValue {name {defvalue {}}} {
00669 ::set value [ncgi::value $name]
00670 ::if {[string length $value] == 0} {
00671 ::set value $defvalue
00672 }
00673 return "name=\"$name\" value=\"[quoteFormValue $value]\""
00674 }
00675
00676 # ::html::quoteFormValue
00677 #
00678 # Quote a value for use in a value=\"$value\" fragment.
00679 #
00680 # Arguments:
00681 # value The value to quote
00682 #
00683 # Retults:
00684 # A string like:
00685 # "Hello, <b>World!"
00686
00687 proc ::html::quoteFormValue {value} {
00688 return [string map [list "&" "&" "\"" """ \
00689 "'" "'" "<" "<" ">" ">"] $value]
00690 }
00691
00692 # ::html::textInput --
00693 #
00694 # Return an <input type=text> element. This uses the
00695 # input.size default falue.
00696 #
00697 # Arguments:
00698 # name The form element name
00699 # args Additional attributes for the INPUT tag
00700 #
00701 # Results:
00702 # The html fragment
00703
00704 proc ::html::textInput {name {value {}} args} {
00705 ::set html "<input type=\"text\" "
00706 append html [formValue $name $value]
00707 append html [default input.size $args]
00708 ::if {[llength $args] != 0} then {
00709 append html " " [join $args]
00710 }
00711 append html ">\n"
00712 return $html
00713 }
00714
00715 # ::html::textInputRow --
00716 #
00717 # Format a table row containing a text input element and a label.
00718 #
00719 # Arguments:
00720 # label Label to display next to the form element
00721 # name The form element name
00722 # args Additional attributes for the INPUT tag
00723 #
00724 # Results:
00725 # The html fragment
00726
00727 proc ::html::textInputRow {label name {value {}} args} {
00728 ::set html [row $label [::eval [linsert $args 0 html::textInput $name $value]]]
00729 return $html
00730 }
00731
00732 # ::html::passwordInputRow --
00733 #
00734 # Format a table row containing a password input element and a label.
00735 #
00736 # Arguments:
00737 # label Label to display next to the form element
00738 # name The form element name
00739 #
00740 # Results:
00741 # The html fragment
00742
00743 proc ::html::passwordInputRow {label {name password}} {
00744 ::set html [row $label [passwordInput $name]]
00745 return $html
00746 }
00747
00748 # ::html::passwordInput --
00749 #
00750 # Return an <input type=password> element.
00751 #
00752 # Arguments:
00753 # name The form element name. Defaults to "password"
00754 #
00755 # Results:
00756 # The html fragment
00757
00758 proc ::html::passwordInput {{name password}} {
00759 ::set html "<input type=\"password\" name=\"$name\">\n"
00760 return $html
00761 }
00762
00763 # ::html::checkbox --
00764 #
00765 # Format a checkbox so that it retains its state based on
00766 # the current CGI values
00767 #
00768 # Arguments:
00769 # name The form element name
00770 # value The value associated with the checkbox
00771 #
00772 # Results:
00773 # The html fragment
00774
00775 proc ::html::checkbox {name value} {
00776 ::set html "<input type=\"checkbox\" [checkValue $name $value]>\n"
00777 }
00778
00779 # ::html::checkValue
00780 #
00781 # Like html::formalue, but for checkboxes that need CHECKED
00782 #
00783 # Arguments:
00784 # name The name of the form element
00785 # defvalue A default value to use, if not appears in the CGI
00786 # inputs
00787 #
00788 # Retults:
00789 # A string like:
00790 # name="fred" value="freds value" CHECKED
00791
00792
00793 proc ::html::checkValue {name {value 1}} {
00794 ::foreach v [ncgi::valueList $name] {
00795 ::if {[string compare $value $v] == 0} {
00796 return "name=\"$name\" value=\"[quoteFormValue $value]\" checked"
00797 }
00798 }
00799 return "name=\"$name\" value=\"[quoteFormValue $value]\""
00800 }
00801
00802 # ::html::radioValue
00803 #
00804 # Like html::formValue, but for radioboxes that need CHECKED
00805 #
00806 # Arguments:
00807 # name The name of the form element
00808 # value The value associated with the radio button.
00809 #
00810 # Retults:
00811 # A string like:
00812 # name="fred" value="freds value" CHECKED
00813
00814 proc ::html::radioValue {name value {defaultSelection {}}} {
00815 ::if {[string equal $value [ncgi::value $name $defaultSelection]]} {
00816 return "name=\"$name\" value=\"[quoteFormValue $value]\" checked"
00817 } else {
00818 return "name=\"$name\" value=\"[quoteFormValue $value]\""
00819 }
00820 }
00821
00822 # ::html::radioSet --
00823 #
00824 # Display a set of radio buttons while looking for an existing
00825 # value from the query data, if any.
00826
00827 proc ::html::radioSet {key sep list {defaultSelection {}}} {
00828 ::set html ""
00829 ::set s ""
00830 ::foreach {label v} $list {
00831 append html "$s<input type=\"radio\" [radioValue $key $v $defaultSelection]> $label"
00832 ::set s $sep
00833 }
00834 return $html
00835 }
00836
00837 # ::html::checkSet --
00838 #
00839 # Display a set of check buttons while looking for an existing
00840 # value from the query data, if any.
00841
00842 proc ::html::checkSet {key sep list} {
00843 ::set s ""
00844 ::foreach {label v} $list {
00845 append html "$s<input type=\"checkbox\" [checkValue $key $v]> $label"
00846 ::set s $sep
00847 }
00848 return $html
00849 }
00850
00851 # ::html::select --
00852 #
00853 # Format a <select> element that retains the state of the
00854 # current CGI values.
00855 #
00856 # Arguments:
00857 # name The form element name
00858 # param The various size, multiple parameters for the tag
00859 # choices A simple list of choices
00860 # current Value to assume if nothing is in CGI state
00861 #
00862 # Results:
00863 # The html fragment
00864
00865 proc ::html::select {name param choices {current {}}} {
00866 ::set def [ncgi::valueList $name $current]
00867 ::set html "<select name=\"$name\"[string trimright " $param"]>\n"
00868 ::foreach {label v} $choices {
00869 ::if {[lsearch -exact $def $v] != -1} {
00870 ::set SEL " selected"
00871 } else {
00872 ::set SEL ""
00873 }
00874 append html "<option value=\"$v\"$SEL>$label\n"
00875 }
00876 append html "</select>\n"
00877 return $html
00878 }
00879
00880 # ::html::selectPlain --
00881 #
00882 # Format a <select> element where the values are the same
00883 # as those that are displayed.
00884 #
00885 # Arguments:
00886 # name The form element name
00887 # param Tag parameters
00888 # choices A simple list of choices
00889 #
00890 # Results:
00891 # The html fragment
00892
00893 proc ::html::selectPlain {name param choices {current {}}} {
00894 ::set namevalue {}
00895 ::foreach c $choices {
00896 lappend namevalue $c $c
00897 }
00898 return [select $name $param $namevalue $current]
00899 }
00900
00901 # ::html::textarea --
00902 #
00903 # Format a <textarea> element that retains the state of the
00904 # current CGI values.
00905 #
00906 # Arguments:
00907 # name The form element name
00908 # param The various size, multiple parameters for the tag
00909 # current Value to assume if nothing is in CGI state
00910 #
00911 # Results:
00912 # The html fragment
00913
00914 proc ::html::textarea {name {param {}} {current {}}} {
00915 ::set value [ncgi::value $name $current]
00916 return "<[string trimright \
00917 "textarea name=\"$name\"\
00918 [tagParam textarea $param]"]>$value</textarea>\n"
00919 }
00920
00921 # ::html::submit --
00922 #
00923 # Format a submit button.
00924 #
00925 # Arguments:
00926 # label The string to appear in the submit button.
00927 # name The name for the submit button element
00928 #
00929 # Results:
00930 # The html fragment
00931
00932
00933 proc ::html::submit {label {name submit}} {
00934 ::set html "<input type=\"submit\" name=\"$name\" value=\"$label\">\n"
00935 }
00936
00937 # ::html::varEmpty --
00938 #
00939 # Return true if the variable doesn't exist or is an empty string
00940 #
00941 # Arguments:
00942 # varname Name of the variable
00943 #
00944 # Results:
00945 # 1 if the variable doesn't exist or has the empty value
00946
00947 proc ::html::varEmpty {name} {
00948 upvar 1 $name var
00949 ::if {[info exists var]} {
00950 ::set value $var
00951 } else {
00952 ::set value ""
00953 }
00954 return [expr {[string length [string trim $value]] == 0}]
00955 }
00956
00957 # ::html::getFormInfo --
00958 #
00959 # Generate hidden fields to capture form values.
00960 #
00961 # Arguments:
00962 # args List of elements to save. If this is empty, everything is
00963 # saved in hidden fields. This is a list of string match
00964 # patterns.
00965 #
00966 # Results:
00967 # A bunch of <input type=hidden> elements
00968
00969 proc ::html::getFormInfo {args} {
00970 ::if {[llength $args] == 0} {
00971 ::set args *
00972 }
00973 ::set html ""
00974 ::foreach {n v} [ncgi::nvlist] {
00975 ::foreach pat $args {
00976 ::if {[string match $pat $n]} {
00977 append html "<input type=\"hidden\" name=\"$n\" \
00978 value=\"[quoteFormValue $v]\">\n"
00979 }
00980 }
00981 }
00982 return $html
00983 }
00984
00985 # ::html::h1
00986 # Generate an H1 tag.
00987 #
00988 # Arguments:
00989 # string
00990 # param
00991 #
00992 # Results:
00993 # Formats the tag.
00994
00995 proc ::html::h1 {string {param {}}} {
00996 html::h 1 $string $param
00997 }
00998 proc ::html::h2 {string {param {}}} {
00999 html::h 2 $string $param
01000 }
01001 proc ::html::h3 {string {param {}}} {
01002 html::h 3 $string $param
01003 }
01004 proc ::html::h4 {string {param {}}} {
01005 html::h 4 $string $param
01006 }
01007 proc ::html::h5 {string {param {}}} {
01008 html::h 5 $string $param
01009 }
01010 proc ::html::h6 {string {param {}}} {
01011 html::h 6 $string $param
01012 }
01013 proc ::html::h {level string {param {}}} {
01014 return "<[string trimright "h$level [tagParam h$level $param]"]>$string</h$level>\n"
01015 }
01016
01017 # ::html::openTag
01018 # Remember that a tag is opened so it can be closed later.
01019 # This is used to automatically clean up at the end of a page.
01020 #
01021 # Arguments:
01022 # tag The HTML tag name
01023 # param Any parameters for the tag
01024 #
01025 # Results:
01026 # Formats the tag. Also keeps it around in a per-page stack
01027 # of open tags.
01028
01029 proc ::html::openTag {tag {param {}}} {
01030 variable page
01031 lappend page(stack) $tag
01032 return "<[string trimright "$tag [tagParam $tag $param]"]>"
01033 }
01034
01035 # ::html::closeTag
01036 # Pop a tag from the stack and close it.
01037 #
01038 # Arguments:
01039 # None
01040 #
01041 # Results:
01042 # A close tag. Also pops the stack.
01043
01044 proc ::html::closeTag {} {
01045 variable page
01046 ::if {[info exists page(stack)]} {
01047 ::set top [lindex $page(stack) end]
01048 ::set page(stack) [lreplace $page(stack) end end]
01049 }
01050 ::if {[info exists top] && [string length $top]} {
01051 return </$top>
01052 } else {
01053 return ""
01054 }
01055 }
01056
01057 # ::html::end
01058 #
01059 # Close out all the open tags. Especially useful for
01060 # Tables that do not display at all if they are unclosed.
01061 #
01062 # Arguments:
01063 # None
01064 #
01065 # Results:
01066 # Some number of close HTML tags.
01067
01068 proc ::html::end {} {
01069 variable page
01070 ::set html ""
01071 ::while {[llength $page(stack)]} {
01072 append html [closeTag]\n
01073 }
01074 return $html
01075 }
01076
01077 # ::html::row
01078 #
01079 # Format a table row. If the default font has been set, this
01080 # takes care of wrapping the table cell contents in a font tag.
01081 #
01082 # Arguments:
01083 # args Values to put into the row
01084 #
01085 # Results:
01086 # A <tr><td>...</tr> fragment
01087
01088 proc ::html::row {args} {
01089 ::set html <tr>\n
01090 ::foreach x $args {
01091 append html \t[cell "" $x td]\n
01092 }
01093 append html "</tr>\n"
01094 return $html
01095 }
01096
01097 # ::html::hdrRow
01098 #
01099 # Format a table row. If the default font has been set, this
01100 # takes care of wrapping the table cell contents in a font tag.
01101 #
01102 # Arguments:
01103 # args Values to put into the row
01104 #
01105 # Results:
01106 # A <tr><th>...</tr> fragment
01107
01108 proc ::html::hdrRow {args} {
01109 variable defaults
01110 ::set html <tr>\n
01111 ::foreach x $args {
01112 append html \t[cell "" $x th]\n
01113 }
01114 append html "</tr>\n"
01115 return $html
01116 }
01117
01118 # ::html::paramRow
01119 #
01120 # Format a table row. If the default font has been set, this
01121 # takes care of wrapping the table cell contents in a font tag.
01122 #
01123 # Based on html::row
01124 #
01125 # Arguments:
01126 # list Values to put into the row
01127 # rparam Parameters for row
01128 # cparam Parameters for cells
01129 #
01130 # Results:
01131 # A <tr><td>...</tr> fragment
01132
01133 proc ::html::paramRow {list {rparam {}} {cparam {}}} {
01134 ::set html "<tr $rparam>\n"
01135 ::foreach x $list {
01136 append html \t[cell $cparam $x td]\n
01137 }
01138 append html "</tr>\n"
01139 return $html
01140 }
01141
01142 # ::html::cell
01143 #
01144 # Format a table cell. If the default font has been set, this
01145 # takes care of wrapping the table cell contents in a font tag.
01146 #
01147 # Arguments:
01148 # param Td tag parameters
01149 # value The value to put into the cell
01150 # tag (option) defaults to TD
01151 #
01152 # Results:
01153 # <td>...</td> fragment
01154
01155 proc ::html::cell {param value {tag td}} {
01156 ::set font [font]
01157 ::if {[string length $font]} {
01158 ::set value $font$value</font>
01159 }
01160 return "<[string trimright "$tag $param"]>$value</$tag>"
01161 }
01162
01163 # ::html::tableFromArray
01164 #
01165 # Format a Tcl array into an HTML table
01166 #
01167 # Arguments:
01168 # arrname The name of the array
01169 # param The <table> tag parameters, if any.
01170 # pat A string match pattern for the element keys
01171 #
01172 # Results:
01173 # A <table>
01174
01175 proc ::html::tableFromArray {arrname {param {}} {pat *}} {
01176 upvar 1 $arrname arr
01177 ::set html ""
01178 ::if {[info exists arr]} {
01179 append html "<table $param>\n"
01180 append html "<tr><th colspan=2>$arrname</th></tr>\n"
01181 ::foreach name [lsort [array names arr $pat]] {
01182 append html [row $name $arr($name)]
01183 }
01184 append html </table>\n
01185 }
01186 return $html
01187 }
01188
01189 # ::html::tableFromList
01190 #
01191 # Format a table from a name, value list
01192 #
01193 # Arguments:
01194 # querylist A name, value list
01195 # param The <table> tag parameters, if any.
01196 #
01197 # Results:
01198 # A <table>
01199
01200 proc ::html::tableFromList {querylist {param {}}} {
01201 ::set html ""
01202 ::if {[llength $querylist]} {
01203 append html "<table $param>"
01204 ::foreach {label value} $querylist {
01205 append html [row $label $value]
01206 }
01207 append html </table>
01208 }
01209 return $html
01210 }
01211
01212 # ::html::mailto
01213 #
01214 # Format a mailto: HREF tag
01215 #
01216 # Arguments:
01217 # email The target
01218 # subject The subject of the email, if any
01219 #
01220 # Results:
01221 # A <a href=mailto> tag </a>
01222
01223 proc ::html::mailto {email {subject {}}} {
01224 ::set html "<a href=\"mailto:$email"
01225 ::if {[string length $subject]} {
01226 append html ?subject=$subject
01227 }
01228 append html "\">$email</a>"
01229 return $html
01230 }
01231
01232 # ::html::font
01233 #
01234 # Generate a standard <font> tag. This depends on defaults being
01235 # set via html::init
01236 #
01237 # Arguments:
01238 # args Font parameters.
01239 #
01240 # Results:
01241 # HTML
01242
01243 proc ::html::font {args} {
01244
01245 # e.g., font.face, font.size, font.color
01246 ::set param [tagParam font [join $args]]
01247
01248 ::if {[string length $param]} {
01249 return "<[string trimright "font $param"]>"
01250 } else {
01251 return ""
01252 }
01253 }
01254
01255 # ::html::minorMenu
01256 #
01257 # Create a menu of links given a list of label, URL pairs.
01258 # If the URL is the current page, it is not highlighted.
01259 #
01260 # Arguments:
01261 #
01262 # list List that alternates label, url, label, url
01263 # sep Separator between elements
01264 #
01265 # Results:
01266 # html
01267
01268 proc ::html::minorMenu {list {sep { | }}} {
01269 ::set s ""
01270 ::set html ""
01271 regsub -- {index.h?tml$} [ncgi::urlStub] {} this
01272 ::foreach {label url} $list {
01273 regsub -- {index.h?tml$} $url {} that
01274 ::if {[string compare $this $that] == 0} {
01275 append html "$s$label"
01276 } else {
01277 append html "$s<a href=\"$url\">$label</a>"
01278 }
01279 ::set s $sep
01280 }
01281 return $html
01282 }
01283
01284 # ::html::minorList
01285 #
01286 # Create a list of links given a list of label, URL pairs.
01287 # If the URL is the current page, it is not highlighted.
01288 #
01289 # Based on html::minorMenu
01290 #
01291 # Arguments:
01292 #
01293 # list List that alternates label, url, label, url
01294 # ordered Boolean flag to choose between ordered and
01295 # unordered lists. Defaults to 0, i.e. unordered.
01296 #
01297 # Results:
01298 # A <ul><li><a...><\li>.....<\ul> fragment
01299 # or a <ol><li><a...><\li>.....<\ol> fragment
01300
01301 proc ::html::minorList {list {ordered 0}} {
01302 ::set s ""
01303 ::set html ""
01304 ::if { $ordered } {
01305 append html [openTag ol]
01306 } else {
01307 append html [openTag ul]
01308 }
01309 regsub -- {index.h?tml$} [ncgi::urlStub] {} this
01310 ::foreach {label url} $list {
01311 append html [openTag li]
01312 regsub -- {index.h?tml$} $url {} that
01313 ::if {[string compare $this $that] == 0} {
01314 append html "$s$label"
01315 } else {
01316 append html "$s<a href=\"$url\">$label</a>"
01317 }
01318 append html [closeTag]
01319 append html \n
01320 }
01321 append html [closeTag]
01322 return $html
01323 }
01324
01325 # ::html::extractParam
01326 #
01327 # Extract a value from parameter list (this needs a re-do)
01328 #
01329 # Arguments:
01330 # param A parameter list. It should alredy have been processed to
01331 # remove any entity references
01332 # key The parameter name
01333 # varName The variable to put the value into (use key as default)
01334 #
01335 # Results:
01336 # returns "1" if the keyword is found, "0" otherwise
01337
01338 proc ::html::extractParam {param key {varName ""}} {
01339 ::if {$varName == ""} {
01340 upvar $key result
01341 } else {
01342 upvar $varName result
01343 }
01344 ::set ws " \t\n\r"
01345
01346 # look for name=value combinations. Either (') or (") are valid delimeters
01347 ::if {
01348 [regsub -nocase [format {.*%s[%s]*=[%s]*"([^"]*).*} $key $ws $ws] $param {\1} value] ||
01349 [regsub -nocase [format {.*%s[%s]*=[%s]*'([^']*).*} $key $ws $ws] $param {\1} value] ||
01350 [regsub -nocase [format {.*%s[%s]*=[%s]*([^%s]+).*} $key $ws $ws $ws] $param {\1} value] } {
01351 ::set result $value
01352 return 1
01353 }
01354
01355 # now look for valueless names
01356 # I should strip out name=value pairs, so we don't end up with "name"
01357 # inside the "value" part of some other key word - some day
01358
01359 ::set bad \[^a-zA-Z\]+
01360 ::if {[regexp -nocase "$bad$key$bad" -$param-]} {
01361 return 1
01362 } else {
01363 return 0
01364 }
01365 }
01366
01367 # ::html::urlParent --
01368 # This is like "file dirname", but doesn't screw with the slashes
01369 # (file dirname will collapse // into /)
01370 #
01371 # Arguments:
01372 # url The URL
01373 #
01374 # Results:
01375 # The parent directory of the URL.
01376
01377 proc ::html::urlParent {url} {
01378 ::set url [string trimright $url /]
01379 regsub -- {[^/]+$} $url {} url
01380 return $url
01381 }
01382
01383 # ::html::html_entities --
01384 # Replaces all special characters in the text with their
01385 # entities.
01386 #
01387 # Arguments:
01388 # s The near-HTML text
01389 #
01390 # Results:
01391 # The text with entities in place of specials characters.
01392
01393 proc ::html::html_entities {s} {
01394 variable entities
01395 return [string map $entities $s]
01396 }
01397
01398 # ::html::nl2br --
01399 # Replaces all line-endings in the text with <br> tags.
01400 #
01401 # Arguments:
01402 # s The near-HTML text
01403 #
01404 # Results:
01405 # The text with <br> in place of line-endings.
01406
01407 proc ::html::nl2br {s} {
01408 return [string map [list \n\r <br> \n <br> \r <br>] $s]
01409 }
01410
01411 # ::html::doctype
01412 # Create the DOCTYPE tag and tuck it away for usage
01413 #
01414 # Arguments:
01415 # arg The DOCTYPE you want to declare
01416 #
01417 # Results:
01418 # HTML for the doctype section
01419
01420 proc ::html::doctype {arg} {
01421 variable doctypes
01422 set code [string toupper $arg]
01423 if {![info exists doctypes($code)]} {
01424 return -code error "Unknown doctype \"$arg\""
01425 }
01426 return $doctypes($code)
01427 }
01428
01429 namespace eval ::html {
01430 variable doctypes
01431 array set doctypes {
01432 HTML32 {<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">}
01433 HTML40 {<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">}
01434 HTML40T {<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">}
01435 HTML40F {<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">}
01436 HTML401 {<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">}
01437 HTML401T {<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">}
01438 HTML401F {<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">}
01439 XHTML10S {<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">}
01440 XHTML10T {<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">}
01441 XHTML10F {<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">}
01442 XHTML11 {<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">}
01443 XHTMLB {<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.0//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">}
01444 }
01445 }
01446
01447 # ::html::css
01448 # Create the text/css tag and tuck it away for usage
01449 #
01450 # Arguments:
01451 # href The location of the css file to include the filename and path
01452 #
01453 # Results:
01454 # HTML for the section
01455
01456 proc ::html::css {href} {
01457 variable page
01458 set page(css) \
01459 "<link rel=\"stylesheet\" type=\"text/css\" href=\"[quoteFormValue $href]\">\n"
01460 return
01461 }
01462
01463 # ::html::js
01464 # Create the text/javascript tag and tuck it away for usage
01465 #
01466 # Arguments:
01467 # href The location of the javascript file to include the filename and path
01468 #
01469 # Results:
01470 # HTML for the section
01471
01472 proc ::html::js {href} {
01473 variable page
01474 set page(js) \
01475 "<script language=\"javascript\" type=\"text/javascript\" src=\"[quoteFormValue $href]\"></script>\n"
01476 return
01477 }
01478