# ACTIVESTATE TEAPOT-PKG BEGIN TM -*- tcl -*- # -- Tcl Module # @@ Meta Begin # Package hv3 0.1 # Meta as::author {Richard Hipp} {Dan Kennedy} # Meta as::build::date 2013-12-16 # Meta as::origin http://tkhtml.tcl.tk # Meta category HTML # Meta description HTML viewer megawidet (hv3 application core) # Meta license LGPL # Meta platform tcl # Meta require {Tcl 8.4} # Meta require {Tk 8.4} # Meta require {Tkhtml 3} # Meta require snit # Meta subject Tk HTML rendering display show # Meta summary HTML viewer widget # @@ Meta End # ACTIVESTATE TEAPOT-PKG BEGIN REQUIREMENTS package require Tcl 8.4 package require Tk 8.4 package require Tkhtml 3 package require snit # ACTIVESTATE TEAPOT-PKG END REQUIREMENTS # ACTIVESTATE TEAPOT-PKG BEGIN DECLARE package provide hv3 0.1 # ACTIVESTATE TEAPOT-PKG END DECLARE # ACTIVESTATE TEAPOT-PKG END TM package provide hv3 0.1 namespace eval hv3 { set {version($Id: hv3_util.tcl,v 1.9 2008/02/02 17:15:02 danielk1977 Exp $)} 1 } namespace eval hv3 { proc ReturnWithArgs {retval args} { return $retval } proc scrollbar {args} { set w [eval [linsert $args 0 ::scrollbar]] $w configure -highlightthickness 0 $w configure -borderwidth 1 return $w } # scrolledwidget # # Widget to add automatic scrollbars to a widget supporting the # [xview], [yview], -xscrollcommand and -yscrollcommand interface (e.g. # html, canvas or text). # namespace eval scrolledwidget { proc new {me widget args} { upvar #0 $me O set w $O(win) set O(-propagate) 0 set O(-scrollbarpolicy) auto set O(-takefocus) 0 set O(myTakeControlCb) "" # Create the three widgets - one user widget and two scrollbars. set O(myWidget) [eval [linsert $widget 1 ${w}.widget]] set O(myVsb) [::hv3::scrollbar ${w}.vsb -orient vertical -takefocus 0] set O(myHsb) [::hv3::scrollbar ${w}.hsb -orient horizontal -takefocus 0] set wid $O(myWidget) bind $w [list $me scrollme $wid yview scroll -1 units] bind $w [list $me scrollme $wid yview scroll 1 units] bind $w [list $me scrollme $wid yview scroll 1 units] bind $w [list $me scrollme $wid xview scroll 1 units] bind $w [list $me scrollme $wid xview scroll -1 units] bind $w [list $me scrollme $wid yview scroll 1 pages] bind $w [list $me scrollme $wid yview scroll 1 pages] bind $w [list $me scrollme $wid yview scroll -1 pages] $O(myVsb) configure -cursor "top_left_arrow" $O(myHsb) configure -cursor "top_left_arrow" grid configure $O(myWidget) -column 0 -row 1 -sticky nsew grid columnconfigure $w 0 -weight 1 grid rowconfigure $w 1 -weight 1 grid propagate $w $O(-propagate) # First, set the values of -width and -height to the defaults for # the scrolled widget class. Then configure this widget with the # arguments provided. $me configure -width [$O(myWidget) cget -width] $me configure -height [$O(myWidget) cget -height] eval $me configure $args # Wire up the scrollbars using the standard Tk idiom. $O(myWidget) configure -yscrollcommand [list $me scrollcallback $O(myVsb)] $O(myWidget) configure -xscrollcommand [list $me scrollcallback $O(myHsb)] $O(myVsb) configure -command [list $me scrollme $O(myWidget) yview] $O(myHsb) configure -command [list $me scrollme $O(myWidget) xview] # Propagate events from the scrolled widget to this one. bindtags $O(myWidget) [concat [bindtags $O(myWidget)] $O(win)] } proc destroy {me} { uplevel #0 [list unset $me] rename $me "" } proc configure-propagate {me} { upvar #0 $me O grid propagate $O(win) $O(-propagate) } proc take_control {me callback} { upvar #0 $me O if {$O(myTakeControlCb) ne ""} { uplevel #0 $O(myTakeControlCb) } set O(myTakeControlCb) $callback } proc scrollme {me args} { upvar #0 $me O if {$O(myTakeControlCb) ne ""} { uplevel #0 $O(myTakeControlCb) set O(myTakeControlCb) "" } eval $args } proc scrollcallback {me scrollbar first last} { upvar #0 $me O $scrollbar set $first $last set ismapped [expr [winfo ismapped $scrollbar] ? 1 : 0] if {$O(-scrollbarpolicy) eq "auto"} { set isrequired [expr ($first == 0.0 && $last == 1.0) ? 0 : 1] } else { set isrequired $O(-scrollbarpolicy) } if {$isrequired && !$ismapped} { switch [$scrollbar cget -orient] { vertical {grid configure $scrollbar -column 1 -row 1 -sticky ns} horizontal {grid configure $scrollbar -column 0 -row 2 -sticky ew} } } elseif {$ismapped && !$isrequired} { grid forget $scrollbar } } proc configure-scrollbarpolicy {me} { upvar #0 $me O eval $me scrollcallback $O(myHsb) [$O(myWidget) xview] eval $me scrollcallback $O(myVsb) [$O(myWidget) yview] } proc widget {me} { upvar #0 $me O return $O(myWidget) } proc unknown {method me args} { # puts "UNKNOWN: $me $method $args" upvar #0 $me O uplevel 3 [list eval $O(myWidget) $method $args] } namespace unknown unknown set DelegateOption(-width) hull set DelegateOption(-height) hull set DelegateOption(-cursor) hull set DelegateOption(*) myWidget } # Wrapper around the ::hv3::scrolledwidget constructor. # # Example usage to create a 400x400 canvas widget named ".c" with # automatic scrollbars: # # ::hv3::scrolled canvas .c -width 400 -height 400 # proc scrolled {widget name args} { return [eval [concat ::hv3::scrolledwidget $name $widget $args]] } proc Expand {template args} { return [string map $args $template] } } namespace eval ::hv3::string { # A generic tokeniser procedure for strings. This proc splits the # input string $input into a list of tokens, where each token is either: # # * A continuous set of alpha-numeric characters, or # * A quoted string (quoted by " or '), or # * Any single character. # # White-space characters are not returned in the list of tokens. # proc tokenise {input} { set tokens [list] set zIn [string trim $input] while {[string length $zIn] > 0} { if {[ regexp {^([[:alnum:]_.-]+)(.*)$} $zIn -> zToken zIn ]} { # Contiguous alpha-numeric characters lappend tokens $zToken } elseif {[ regexp {^(["'])} $zIn -> zQuote]} { #;'" # Quoted string set nEsc 0 for {set nToken 1} {$nToken < [string length $zIn]} {incr nToken} { set c [string range $zIn $nToken $nToken] if {$c eq $zQuote && 0 == ($nEsc%2)} break set nEsc [expr {($c eq "\\") ? $nEsc+1 : 0}] } set zToken [string range $zIn 0 $nToken] set zIn [string range $zIn [expr {$nToken+1}] end] lappend tokens $zToken } else { lappend tokens [string range $zIn 0 0] set zIn [string range $zIn 1 end] } set zIn [string trimleft $zIn] } return $tokens } # Dequote $input, if it appears to be a quoted string (starts with # a single or double quote character). # proc dequote {input} { set zIn $input set zQuote [string range $zIn 0 0] if {$zQuote eq "\"" || $zQuote eq "\'"} { set zIn [string range $zIn 1 end] if {[string range $zIn end end] eq $zQuote} { set zIn [string range $zIn 0 end-1] } set zIn [regsub {\\(.)} $zIn {\1}] } return $zIn } # A procedure to parse an HTTP content-type (media type). See section # 3.7 of the http 1.1 specification. # # A list of exactly three elements is returned. These are the type, # subtype and charset as specified in the parsed content-type. Any or # all of the fields may be empty strings, if they are not present in # the input or a parse error occurs. # proc parseContentType {contenttype} { set tokens [::hv3::string::tokenise $contenttype] set type [lindex $tokens 0] set subtype [lindex $tokens 2] set enc "" foreach idx [lsearch -regexp -all $tokens (?i)charset] { if {[lindex $tokens [expr {$idx+1}]] eq "="} { set enc [::hv3::string::dequote [lindex $tokens [expr {$idx+2}]]] break } } return [list $type $subtype $enc] } proc htmlize {zIn} { string map [list "<" "<" ">" ">" "&" "&" "\"" ""e;"] $zIn } } proc ::hv3::char {text idx} { return [string range $text $idx $idx] } proc ::hv3::next_word {text idx idx_out} { while {[char $text $idx] eq " "} { incr idx } set idx2 $idx set c [char $text $idx2] if {$c eq "\""} { # Quoted identifier incr idx2 set c [char $text $idx2] while {$c ne "\"" && $c ne ""} { incr idx2 set c [char $text $idx2] } incr idx2 set word [string range $text [expr $idx+1] [expr $idx2 - 2]] } else { # Unquoted identifier while {$c ne ">" && $c ne " " && $c ne ""} { incr idx2 set c [char $text $idx2] } set word [string range $text $idx [expr $idx2 - 1]] } uplevel [list set $idx_out $idx2] return $word } proc ::hv3::sniff_doctype {text pIsXhtml} { upvar $pIsXhtml isXHTML # set QuirksmodeIdentifiers [list \ "-//w3c//dtd html 4.01 transitional//en" \ "-//w3c//dtd html 4.01 frameset//en" \ "-//w3c//dtd html 4.0 transitional//en" \ "-//w3c//dtd html 4.0 frameset//en" \ "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//en" \ "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//en" \ "-//ietf//dtd html//en//3.0" \ "-//w3o//dtd w3 html 3.0//en//" \ "-//w3o//dtd w3 html 3.0//en" \ "-//w3c//dtd html 3 1995-03-24//en" \ "-//ietf//dtd html 3.0//en" \ "-//ietf//dtd html 3.0//en//" \ "-//ietf//dtd html 3//en" \ "-//ietf//dtd html level 3//en" \ "-//ietf//dtd html level 3//en//3.0" \ "-//ietf//dtd html 3.2//en" \ "-//as//dtd html 3.0 aswedit + extensions//en" \ "-//advasoft ltd//dtd html 3.0 aswedit + extensions//en" \ "-//ietf//dtd html strict//en//3.0" \ "-//w3o//dtd w3 html strict 3.0//en//" \ "-//ietf//dtd html strict level 3//en" \ "-//ietf//dtd html strict level 3//en//3.0" \ "html" \ "-//ietf//dtd html//en" \ "-//ietf//dtd html//en//2.0" \ "-//ietf//dtd html 2.0//en" \ "-//ietf//dtd html level 2//en" \ "-//ietf//dtd html level 2//en//2.0" \ "-//ietf//dtd html 2.0 level 2//en" \ "-//ietf//dtd html level 1//en" \ "-//ietf//dtd html level 1//en//2.0" \ "-//ietf//dtd html 2.0 level 1//en" \ "-//ietf//dtd html level 0//en" \ "-//ietf//dtd html level 0//en//2.0" \ "-//ietf//dtd html strict//en" \ "-//ietf//dtd html strict//en//2.0" \ "-//ietf//dtd html strict level 2//en" \ "-//ietf//dtd html strict level 2//en//2.0" \ "-//ietf//dtd html 2.0 strict//en" \ "-//ietf//dtd html 2.0 strict level 2//en" \ "-//ietf//dtd html strict level 1//en" \ "-//ietf//dtd html strict level 1//en//2.0" \ "-//ietf//dtd html 2.0 strict level 1//en" \ "-//ietf//dtd html strict level 0//en" \ "-//ietf//dtd html strict level 0//en//2.0" \ "-//webtechs//dtd mozilla html//en" \ "-//webtechs//dtd mozilla html 2.0//en" \ "-//netscape comm. corp.//dtd html//en" \ "-//netscape comm. corp.//dtd html//en" \ "-//netscape comm. corp.//dtd strict html//en" \ "-//microsoft//dtd internet explorer 2.0 html//en" \ "-//microsoft//dtd internet explorer 2.0 html strict//en" \ "-//microsoft//dtd internet explorer 2.0 tables//en" \ "-//microsoft//dtd internet explorer 3.0 html//en" \ "-//microsoft//dtd internet explorer 3.0 html strict//en" \ "-//microsoft//dtd internet explorer 3.0 tables//en" \ "-//sun microsystems corp.//dtd hotjava html//en" \ "-//sun microsystems corp.//dtd hotjava strict html//en" \ "-//ietf//dtd html 2.1e//en" \ "-//o'reilly and associates//dtd html extended 1.0//en" \ "-//o'reilly and associates//dtd html extended relaxed 1.0//en" \ "-//o'reilly and associates//dtd html 2.0//en" \ "-//sq//dtd html 2.0 hotmetal + extensions//en" \ "-//spyglass//dtd html 2.0 extended//en" \ "+//silmaril//dtd html pro v0r11 19970101//en" \ "-//w3c//dtd html experimental 19960712//en" \ "-//w3c//dtd html 3.2//en" \ "-//w3c//dtd html 3.2 final//en" \ "-//w3c//dtd html 3.2 draft//en" \ "-//w3c//dtd html experimental 970421//en" \ "-//w3c//dtd html 3.2s draft//en" \ "-//w3c//dtd w3 html//en" \ "-//metrius//dtd metrius presentational//en" \ ] set isXHTML 0 set idx [string first [set $ii]" # } # Figure out if this should be handled as XHTML # if {[string first xhtml $Identifier] >= 0} { set isXHTML 1 } if {$Availability eq "public"} { set s [expr [string length $Url] > 0] if { $Identifier eq "-//w3c//dtd xhtml 1.0 transitional//en" || $Identifier eq "-//w3c//dtd xhtml 1.0 frameset//en" || ($s && $Identifier eq "-//w3c//dtd html 4.01 transitional//en") || ($s && $Identifier eq "-//w3c//dtd html 4.01 frameset//en") } { return "almost standards" } if {[lsearch $QuirksmodeIdentifiers $Identifier] >= 0} { return "quirks" } } return "standards" } proc ::hv3::configure_doctype_mode {html text pIsXhtml} { upvar $pIsXhtml isXHTML set mode [sniff_doctype $text isXHTML] switch -- $mode { "quirks" { set defstyle [::tkhtml::htmlstyle -quirks] } "almost standards" { set defstyle [::tkhtml::htmlstyle] } "standards" { set defstyle [::tkhtml::htmlstyle] } } $html configure -defaultstyle $defstyle -mode $mode return $mode } namespace eval ::hv3 { variable Counter 1 proc handle_destroy {me obj win} { if {$obj eq $win} { upvar #0 $me O set cmd $O(cmd) $me destroy rename $cmd "" } } proc handle_rename {me oldname newname op} { upvar #0 $me O set O(cmd) $newname } proc construct_object {ns obj arglist} { set PROC proc if {[info commands real_proc] ne ""} { set PROC real_proc } set isWidget [expr {[string range $obj 0 0] eq "."}] # The name of the array to use for this object. set arrayname $obj if {$arrayname eq "%AUTO%" || $isWidget} { set arrayname ${ns}::inst[incr ${ns}::_OBJ_COUNTER] } # Create the object command. set body "namespace eval $ns \$m $arrayname \$args" namespace eval :: [list $PROC $arrayname {m args} $body] # If the first character of the new command name is ".", then # this is a new widget. Populate the state array with the following # special variables: # # O(win) Window path. # O(hull) Window command. # if {[string range $obj 0 0] eq "."} { variable HullType variable Counter upvar #0 $arrayname O set O(hull) ${obj}_win[incr Counter] set O(win) $obj eval $HullType($ns) $O(win) namespace eval :: rename $O(win) $O(hull) bind $obj +[list ::hv3::handle_destroy $arrayname $obj %W] namespace eval :: [list $PROC $O(win) {m args} $body] set O(cmd) $O(win) trace add command $O(win) rename [list ::hv3::handle_rename $arrayname] } # Call the object constructor. namespace eval $ns new $arrayname $arglist return [expr {$isWidget ? $obj : $arrayname}] } proc make_constructor {ns {hulltype frame}} { variable HullType if {[info commands ${ns}::destroy] eq ""} { error "Object class has no destructor: $ns" } set HullType($ns) $hulltype # Create the constructor # proc $ns {obj args} "::hv3::construct_object $ns \$obj \$args" # Create the [cget] method. # namespace eval $ns " proc cget {me option} { upvar \$me O if {!\[info exists O(\$option)\]} { variable DelegateOption if {\[info exists DelegateOption(\$option)\]} { return \[ eval \$O(\$DelegateOption(\$option)) [list cget \$option] \] return } elseif {\[info exists DelegateOption(*)\]} { return \[eval \$O(\$DelegateOption(*)) [list cget \$option ]\] } error \"unknown option: \$option\" } return \$O(\$option) } " # Create the [configure] method. # set cc "" foreach cmd [info commands ${ns}::configure*] { set key [string range $cmd [string length ${ns}::configure] end] append cc "if {\$option eq {$key}} {configure$key \$me}\n" } namespace eval $ns " proc configure {me args} { upvar \$me O foreach {option value} \$args { if {!\[info exists O(\$option)\]} { variable DelegateOption if {\[info exists DelegateOption(\$option)\]} { eval \$O(\$DelegateOption(\$option)) [list configure \$option \$value] } elseif {\[info exists DelegateOption(*)\]} { eval \$O(\$DelegateOption(*)) [list configure \$option \$value] } else { error \"unknown option: \$option\" } } elseif {\$O(\$option) != \$value} { set O(\$option) \$value $cc } } } " } } ::hv3::make_constructor ::hv3::scrolledwidget namespace eval hv3 { set {version($Id: hv3.tcl,v 1.248 2008/03/02 15:00:13 danielk1977 Exp $)} 1 } # This file contains the mega-widget hv3::hv3 that is at the core # of the Hv3 web browser implementation. An instance of this widget # displays a single HTML frame. Documentation for the published # interface to this widget is found at: # # http://tkhtml.tcl.tk/hv3_widget.html # # Other parts of the interface, used internally and by the Hv3 # web-browser, are documented in comments in this file. Eventually, # the Hv3 web-browser will use the published interface only. But # that is not the case yet. # #------------------------------------------------------------------- # # # # Standard Functionality: # # xview # yview # -xscrollcommand # -yscrollcommand # -width # -height # # Widget Specific Options: # # -requestcmd # If not an empty string, this option specifies a script to be # invoked for a GET or POST request. The script is invoked with a # download handle appended to it. See the description of class # ::hv3::request for a description. # # -targetcmd # If not an empty string, this option specifies a script for # the widget to invoke when a hyperlink is clicked on or a form # submitted. The script is invoked with the node handle of the # clicked hyper-link element appended. The script must return # the name of an hv3 widget to load the new document into. This # is intended to be used to implement frameset handling. # # -isvisitedcmd # If not an empty string, this option specifies a script for # the widget to invoke to determine if a hyperlink node should # be styled with the :link or :visited pseudo-class. The # script is invoked with the node handle appended to it. If # true is returned, :visited is used, otherwise :link. # # -fonttable # Delegated through to the html widget. # # -locationvar # Set to the URI of the currently displayed document. # # -scrollbarpolicy # This option may be set to either a boolean value or "auto". It # determines the visibility of the widget scrollbars. TODO: This # is now set internally by the value of the "overflow" property # on the root element. Maybe the option should be removed? # # # Widget Sub-commands: # # goto URI ?OPTIONS? # Load the content at the specified URI into the widget. # # stop # Cancel all pending downloads. # # node # Caching wrapper around html widget [node] command. # # reset # Wrapper around the html widget command of the same name. Also # resets all document related state stored by the mega-widget. # # html # Return the path of the underlying html widget. This should only # be used to determine paths for child widgets. Bypassing hv3 and # accessing the html widget interface directly may confuse hv3. # # title # Return the "title" of the currently loaded document. # # location # Return the location URI of the widget. # # selected # Return the currently selected text, or an empty string if no # text is currently selected. # # # Widget Custom Events: # # <> # This event is generated whenever the goto method is called. # # <> # This event is generated once all of the resources required # to display a document have been loaded. This is analogous # to the Html "onload" event. # # <> # This event is generated whenever the "location" is set. # # <> # Generated whenever the widget state should be saved. # # The code in this file is partitioned into the following classes: # # ::hv3::hv3 # ::hv3::selectionmanager # ::hv3::dynamicmanager # ::hv3::hyperlinkmanager # ::hv3::mousemanager # # ::hv3::hv3 is, of course, the main mega-widget class. Class # ::hv3::request is part of the public interface to ::hv3::hv3. A # single instance of ::hv3::request represents a resource request made # by the mega-widget package - for document, stylesheet, image or # object data. # # The three "manager" classes all implement the following interface. Each # ::hv3::hv3 widget has exactly one of each manager class as a component. # Further manager objects may be added in the future. Interface: # # set manager [::hv3::XXXmanager $hv3] # # $manager motion X Y # $manager release X Y # $manager press X Y # # The -targetcmd option of ::hv3::hv3 is delegated to the # ::hv3::hyperlinkmanager component. # package require Tkhtml 3.0 package require snit package provide hv3 0.1 if {[info commands ::hv3::make_constructor] eq ""} { source [file join [file dirname [info script]] hv3_encodings.tcl] source [file join [file dirname [info script]] hv3_util.tcl] source [file join [file dirname [info script]] hv3_form.tcl] source [file join [file dirname [info script]] hv3_request.tcl] } #source [file join [file dirname [info script]] hv3_request.tcl.bak] #-------------------------------------------------------------------------- # Class ::hv3::hv3::mousemanager # # This type contains code for the ::hv3::hv3 widget to manage # dispatching mouse events that occur in the HTML widget to the # rest of the application. The following HTML4 events are handled: # # Pointer movement: # onmouseover # onmouseout # motion # # Click-related events: # onmousedown # onmouseup # onclick # # Currently, the following hv3 subsystems subscribe to one or more of # these events: # # ::hv3::hyperlinkmanager # Click events, mouseover and mouseout on all nodes. # # ::hv3::dynamicmanager # Events mouseover, mouseout, mousedown mouseup on all nodes. # # ::hv3::formmanager # Click events (for clickable controls) on all nodes. # # ::hv3::selectionmanager # motion # namespace eval ::hv3::hv3::mousemanager { proc new {me hv3} { upvar $me O set O(myHv3) $hv3 set O(myHtml) [$hv3 html] # In browsers with no DOM support, the following option is set to # an empty string. # # If not set to an empty string, this option is set to the name # of the ::hv3::dom object to dispatch events too. The DOM # is a special client because it may cancel the "default action" # of mouse-clicks (it may also cancel other events, but they are # dispatched by other sub-systems). # # Each time an event occurs, the following script is executed: # # $O(-dom) mouseevent EVENT-TYPE NODE X Y ?OPTIONS? # # where OPTIONS are: # # -button INTEGER (default 0) # -detail INTEGER (default 0) # -relatedtarget NODE-HANDLE (default "") # # the EVENT-TYPE parameter is one of: # # "click", "mouseup", "mousedown", "mouseover" or "mouseout". # # NODE is the target leaf node and X and Y are the pointer coordinates # relative to the top-left of the html widget window. # # For "click" events, if the $O(-dom) script returns false, then # the "click" event is not dispatched to any subscribers (this happens # when some javascript calls the Event.preventDefault() method). If it # returns true, proceed as normal. Other event types ignore the return # value of the $O(-dom) script. # set O(-dom) "" # This variable is set to the node-handle that the pointer is currently # hovered over. Used by code that dispatches the "mouseout", "mouseover" # and "mousemove" to the DOM. # set O(myCurrentDomNode) "" # The "top" node from the ${me}.hovernodes array. This is the node # that determines the pointer to display (via the CSS2 'cursor' # property). # set O(myTopHoverNode) "" set O(myCursor) "" set O(myCursorWin) [$hv3 hull] # Database of callback scripts for each event type. # set O(scripts.onmouseover) "" set O(scripts.onmouseout) "" set O(scripts.onclick) "" set O(scripts.onmousedown) "" set O(scripts.onmouseup) "" set O(scripts.motion) "" # There are also two arrays that store lists of nodes currently "hovered" # over and "active". An entry in the correspondoing array indicates the # condition is true. The arrays are named: # # ${me}.hovernodes # ${me}.activenodes # set w [$hv3 win] bind $w "+[list $me Motion %W %x %y]" bind $w "+[list $me Press %W %x %y]" bind $w "+[list $me Release %W %x %y]" } proc subscribe {me event script} { upvar $me O # Check that the $event argument is Ok: if {![info exists O(scripts.$event)]} { error "No such mouse-event: $event" } # Append the script to the callback list. lappend O(scripts.$event) $script } proc reset {me} { upvar $me O array unset ${me}.activenodes array unset ${me}.hovernodes set O(myCurrentDomNode) "" } proc GenerateEvents {me eventlist} { upvar $me O foreach {event node} $eventlist { if {[info commands $node] ne ""} { foreach script $O(scripts.$event) { eval $script $node } } } } proc AdjustCoords {to W xvar yvar} { upvar $xvar x upvar $yvar y while {$W ne "" && $W ne $to} { incr x [winfo x $W] incr y [winfo y $W] set W [winfo parent $W] } } # Mapping from CSS2 cursor type to Tk cursor type. # variable CURSORS array set CURSORS [list \ crosshair crosshair \ default "" \ pointer hand2 \ move fleur \ text xterm \ wait watch \ progress box_spiral \ help question_arrow \ ] proc Motion {me W x y} { upvar $me O variable CURSORS if {$W eq ""} return AdjustCoords [$O(myHv3) html] $W x y # Figure out the node the cursor is currently hovering over. Todo: # When the cursor is over multiple nodes (because overlapping content # has been generated), maybe this should consider all overlapping nodes # as "hovered". set nodelist [lindex [$O(myHtml) node $x $y] end] # Handle the 'cursor' property. # set topnode [lindex $nodelist end] if {$topnode ne "" && $topnode ne $O(myTopHoverNode)} { set Cursor "" if {[$topnode tag] eq ""} { set Cursor xterm set topnode [$topnode parent] } set css2_cursor [$topnode property cursor] catch { set Cursor $CURSORS($css2_cursor) } if {$Cursor ne $O(myCursor)} { $O(myCursorWin) configure -cursor $Cursor set O(myCursor) $Cursor } set O(myTopHoverNode) $topnode } # Dispatch any DOM events in this order: # # mouseout # mouseover # mousemotion # set N [lindex $nodelist end] if {$N eq ""} {set N [$O(myHv3) node]} if {$O(-dom) ne ""} { if {$N ne $O(myCurrentDomNode)} { $O(-dom) mouseevent mouseout $O(myCurrentDomNode) $x $y $O(-dom) mouseevent mouseover $N $x $y set O(myCurrentDomNode) $N } $O(-dom) mouseevent mousemove $N $x $y } foreach script $O(scripts.motion) { eval $script $N $x $y } # After the loop runs, hovernodes will contain the list of # currently hovered nodes. array set hovernodes [list] # Events to generate: set events(onmouseout) [list] set events(onmouseover) [list] foreach node $nodelist { if {[$node tag] eq ""} {set node [$node parent]} for {set n $node} {$n ne ""} {set n [$n parent]} { if {[info exists hovernodes($n)]} { break } else { if {[info exists ${me}.hovernodes($n)]} { unset ${me}.hovernodes($n) } else { lappend events(onmouseover) $n } set hovernodes($n) "" } } } set events(onmouseout) [array names ${me}.hovernodes] array unset ${me}.hovernodes array set ${me}.hovernodes [array get hovernodes] set eventlist [list] foreach key [list onmouseover onmouseout] { foreach node $events($key) { lappend eventlist $key $node } } $me GenerateEvents $eventlist } proc Press {me W x y} { upvar $me O if {$W eq ""} return AdjustCoords [$O(myHv3) html] $W x y set N [lindex [$O(myHtml) node $x $y] end] if {$N ne ""} { if {[$N tag] eq ""} {set N [$N parent]} } if {$N eq ""} {set N [$O(myHv3) node]} # Dispatch the "mousedown" event to the DOM, if any. # set rc "" if {$O(-dom) ne ""} { set rc [$O(-dom) mouseevent mousedown $N $x $y] } # If the DOM implementation called preventDefault(), do # not start selecting text. But every mouseclick should clear # the current selection, otherwise the browser window can get # into an annoying state. # if {$rc eq "prevent"} { $O(myHv3) theselectionmanager clear } else { $O(myHv3) theselectionmanager press $N $x $y } for {set n $N} {$n ne ""} {set n [$n parent]} { set ${me}.activenodes($n) 1 } set eventlist [list] foreach node [array names ${me}.activenodes] { lappend eventlist onmousedown $node } $me GenerateEvents $eventlist } proc Release {me W x y} { upvar $me O if {$W eq ""} return AdjustCoords [$O(myHv3) html] $W x y set N [lindex [$O(myHtml) node $x $y] end] if {$N ne ""} { if {[$N tag] eq ""} {set N [$N parent]} } if {$N eq ""} {set N [$O(myHv3) node]} # Dispatch the "mouseup" event to the DOM, if any. # # In Tk, the equivalent of the "mouseup" () is always # dispatched to the same widget as the "mousedown" (). # But in the DOM things are different - the event target for "mouseup" # depends on the current cursor location only. # if {$O(-dom) ne ""} { $O(-dom) mouseevent mouseup $N $x $y } # Check if the is a "click" event to dispatch to the DOM. If the # ::hv3::dom [mouseevent] method returns 0, then the click is # not sent to the other hv3 sub-systems (default action is cancelled). # set domrc "" if {$O(-dom) ne ""} { for {set n $N} {$n ne ""} {set n [$n parent]} { if {[info exists ${me}.activenodes($N)]} { set domrc [$O(-dom) mouseevent click $n $x $y] break } } } set eventlist [list] foreach node [array names ${me}.activenodes] { lappend eventlist onmouseup $node } if {$domrc ne "prevent"} { set onclick_nodes [list] for {set n $N} {$n ne ""} {set n [$n parent]} { if {[info exists ${me}.activenodes($n)]} { lappend onclick_nodes $n } } foreach node $onclick_nodes { lappend eventlist onclick $node } } $me GenerateEvents $eventlist array unset ${me}.activenodes } proc destroy me { array unset $me array unset ${me}.hovernodes array unset ${me}.activenodes rename $me {} } } ::hv3::make_constructor ::hv3::hv3::mousemanager #-------------------------------------------------------------------------- # ::hv3::hv3::selectionmanager # # This type encapsulates the code that manages selecting text # in the html widget with the mouse. # namespace eval ::hv3::hv3::selectionmanager { proc new {me hv3} { upvar $me O # Variable myMode may take one of the following values: # # "char" -> Currently text selecting by character. # "word" -> Currently text selecting by word. # "block" -> Currently text selecting by block. # set O(myState) false ;# True when left-button is held down set O(myMode) char # The ::hv3::hv3 widget. # set O(myHv3) $hv3 set O(myHtml) [$hv3 html] set O(myFromNode) "" set O(myFromIdx) "" set O(myToNode) "" set O(myToIdx) "" set O(myIgnoreMotion) 0 set w [$hv3 win] selection handle $w [list ::hv3::bg [list $me get_selection]] # bind $myHv3 "+[list $self motion %x %y]" # bind $myHv3 "+[list $self press %x %y]" bind $w "+[list $me doublepress %x %y]" bind $w "+[list $me triplepress %x %y]" bind $w "+[list $me release %x %y]" } # Clear the selection. # proc clear {me} { upvar $me O $O(myHtml) tag delete selection $O(myHtml) tag configure selection -foreground white -background darkgrey set O(myFromNode) "" set O(myToNode) "" } proc press {me N x y} { upvar $me O # Single click -> Select by character. clear $me set O(myState) true set O(myMode) char motion $me $N $x $y } # Given a node-handle/index pair identifying a character in the # current document, return the index values for the start and end # of the word containing the character. # proc ToWord {node idx} { set t [$node text] set cidx [::tkhtml::charoffset $t $idx] set cidx1 [string wordstart $t $cidx] set cidx2 [string wordend $t $cidx] set idx1 [::tkhtml::byteoffset $t $cidx1] set idx2 [::tkhtml::byteoffset $t $cidx2] return [list $idx1 $idx2] } # Add the widget tag "selection" to the word containing the character # identified by the supplied node-handle/index pair. # proc TagWord {me node idx} { upvar $me O foreach {i1 i2} [ToWord $node $idx] {} $O(myHtml) tag add selection $node $i1 $node $i2 } # Remove the widget tag "selection" to the word containing the character # identified by the supplied node-handle/index pair. # proc UntagWord {me node idx} { upvar $me O foreach {i1 i2} [ToWord $node $idx] {} $O(myHtml) tag remove selection $node $i1 $node $i2 } proc ToBlock {me node idx} { upvar $me O set t [$O(myHtml) text text] set offset [$O(myHtml) text offset $node $idx] set start [string last "\n" $t $offset] if {$start < 0} {set start 0} set end [string first "\n" $t $offset] if {$end < 0} {set end [string length $t]} set start_idx [$O(myHtml) text index $start] set end_idx [$O(myHtml) text index $end] return [concat $start_idx $end_idx] } proc TagBlock {me node idx} { upvar $me O foreach {n1 i1 n2 i2} [ToBlock $me $node $idx] {} $O(myHtml) tag add selection $n1 $i1 $n2 $i2 } proc UntagBlock {me node idx} { upvar $me O foreach {n1 i1 n2 i2} [ToBlock $me $node $idx] {} catch {$O(myHtml) tag remove selection $n1 $i1 $n2 $i2} } proc doublepress {me x y} { upvar $me O # Double click -> Select by word. clear $me set O(myMode) word set O(myState) true motion $me "" $x $y } proc triplepress {me x y} { upvar $me O # Triple click -> Select by block. clear $me set O(myMode) block set O(myState) true motion $me "" $x $y } proc release {me x y} { upvar $me O set O(myState) false } proc reset {me} { upvar $me O set O(myState) false # Unset the myFromNode variable, since the node handle it (may) refer # to is now invalid. If this is not done, a future call to the [selected] # method of this object will cause an error by trying to use the # (now invalid) node-handle value in $myFromNode. set O(myFromNode) "" set O(myToNode) "" } proc motion {me N x y} { upvar $me O if {!$O(myState) || $O(myIgnoreMotion)} return set to [$O(myHtml) node -index $x $y] foreach {toNode toIdx} $to {} # $N containst the node-handle for the node that the cursor is # currently hovering over (according to the mousemanager component). # If $N is in a different stacking-context to the closest text, # do not update the highlighted region in this event. # if {$N ne "" && [info exists toNode]} { if {[$N stacking] ne [$toNode stacking]} { set to "" } } if {[llength $to] > 0} { if {$O(myFromNode) eq ""} { set O(myFromNode) $toNode set O(myFromIdx) $toIdx } # This block is where the "selection" tag is added to the HTML # widget (so that the selected text is highlighted). If some # javascript has been messing with the tree, then either or # both of $myFromNode and $myToNode may be orphaned or deleted. # If so, catch the exception and clear the selection. # set rc [catch { if {$O(myToNode) ne $toNode || $toIdx != $O(myToIdx)} { switch -- $O(myMode) { char { if {$O(myToNode) ne ""} { $O(myHtml) tag remove selection $O(myToNode) $O(myToIdx) $toNode $toIdx } $O(myHtml) tag add selection $O(myFromNode) $O(myFromIdx) $toNode $toIdx if {$O(myFromNode) ne $toNode || $O(myFromIdx) != $toIdx} { selection own [$O(myHv3) win] } } word { if {$O(myToNode) ne ""} { $O(myHtml) tag remove selection $O(myToNode) $O(myToIdx) $toNode $toIdx $me UntagWord $O(myToNode) $O(myToIdx) } $O(myHtml) tag add selection $O(myFromNode) $O(myFromIdx) $toNode $toIdx $me TagWord $toNode $toIdx $me TagWord $O(myFromNode) $O(myFromIdx) selection own [$O(myHv3) win] } block { set to_block2 [$me ToBlock $toNode $toIdx] set from_block [$me ToBlock $O(myFromNode) $O(myFromIdx)] if {$O(myToNode) ne ""} { set to_block [$me ToBlock $O(myToNode) $O(myToIdx)] $O(myHtml) tag remove selection $O(myToNode) $O(myToIdx) $toNode $toIdx eval $O(myHtml) tag remove selection $to_block } $O(myHtml) tag add selection $O(myFromNode) $O(myFromIdx) $toNode $toIdx eval $O(myHtml) tag add selection $to_block2 eval $O(myHtml) tag add selection $from_block selection own [$O(myHv3) win] } } set O(myToNode) $toNode set O(myToIdx) $toIdx } } msg] if {$rc && [regexp {[^ ]+ is an orphan} $msg]} { $me clear } } set motioncmd "" set win [$O(myHv3) win] if {$y > [winfo height $win]} { set motioncmd [list yview scroll 1 units] } elseif {$y < 0} { set motioncmd [list yview scroll -1 units] } elseif {$x > [winfo width $win]} { set motioncmd [list xview scroll 1 units] } elseif {$x < 0} { set motioncmd [list xview scroll -1 units] } if {$motioncmd ne ""} { set O(myIgnoreMotion) 1 eval $O(myHv3) $motioncmd after 20 [list $me ContinueMotion] } } proc ContinueMotion {me} { upvar $me O set win [$O(myHv3) win] set O(myIgnoreMotion) 0 set x [expr [winfo pointerx $win] - [winfo rootx $win]] set y [expr [winfo pointery $win] - [winfo rooty $win]] set N [lindex [$O(myHv3) node $x $y] 0] $me motion $N $x $y } # get_selection OFFSET MAXCHARS # # This command is invoked whenever the current selection is selected # while it is owned by the html widget. The text of the selected # region is returned. # proc get_selection {me offset maxChars} { upvar $me O set t [$O(myHv3) html text text] set n1 $O(myFromNode) set i1 $O(myFromIdx) set n2 $O(myToNode) set i2 $O(myToIdx) set stridx_a [$O(myHv3) html text offset $O(myFromNode) $O(myFromIdx)] set stridx_b [$O(myHv3) html text offset $O(myToNode) $O(myToIdx)] if {$stridx_a > $stridx_b} { foreach {stridx_a stridx_b} [list $stridx_b $stridx_a] {} } if {$O(myMode) eq "word"} { set stridx_a [string wordstart $t $stridx_a] set stridx_b [string wordend $t $stridx_b] } if {$O(myMode) eq "block"} { set stridx_a [string last "\n" $t $stridx_a] if {$stridx_a < 0} {set stridx_a 0} set stridx_b [string first "\n" $t $stridx_b] if {$stridx_b < 0} {set stridx_b [string length $t]} } set T [string range $t $stridx_a [expr $stridx_b - 1]] set T [string range $T $offset [expr $offset + $maxChars]] return $T } proc selected {me} { upvar $me O if {$O(myFromNode) eq ""} {return ""} return [$me get_selection 0 10000000] } proc destroy {me} { array unset $me rename $me {} } } ::hv3::make_constructor ::hv3::hv3::selectionmanager # # End of ::hv3::hv3::selectionmanager #-------------------------------------------------------------------------- #-------------------------------------------------------------------------- # Class ::hv3::hv3::dynamicmanager # # This class is responsible for setting the dynamic :hover flag on # document nodes in response to cursor movements. It may one day # be extended to handle :focus and :active, but it's not yet clear # exactly how these should be dealt with. # namespace eval ::hv3::hv3::dynamicmanager { proc new {me hv3} { $hv3 Subscribe onmouseover [list $me handle_mouseover] $hv3 Subscribe onmouseout [list $me handle_mouseout] $hv3 Subscribe onmousedown [list $me handle_mousedown] $hv3 Subscribe onmouseup [list $me handle_mouseup] } proc destroy {me} { uplevel #0 [list unset $me] rename $me "" } proc handle_mouseover {me node} { $node dynamic set hover } proc handle_mouseout {me node} { $node dynamic clear hover } proc handle_mousedown {me node} { $node dynamic set active } proc handle_mouseup {me node} { $node dynamic clear active } } ::hv3::make_constructor ::hv3::hv3::dynamicmanager # # End of ::hv3::hv3::dynamicmanager #-------------------------------------------------------------------------- #-------------------------------------------------------------------------- # Class ::hv3::hv3::hyperlinkmanager # # Each instance of the hv3 widget contains a single hyperlinkmanager as # a component. The hyperlinkmanager takes care of: # # * -targetcmd option and associate callbacks # * -isvisitedcmd option and associate callbacks # * Modifying the cursor to the hand shape when over a hyperlink # * Setting the :link or :visited dynamic condition on hyperlink # elements (depending on the return value of -isvisitedcmd). # # This class installs a node handler for elements. It also subscribes # to the , and events on the # associated hv3 widget. # namespace eval ::hv3::hv3::hyperlinkmanager { proc new {me hv3 baseuri} { upvar $me O set O(myHv3) $hv3 set O(myBaseUri) $baseuri set O(myLinkHoverCount) 0 set O(-targetcmd) [list ::hv3::ReturnWithArgs $hv3] set O(-isvisitedcmd) [list ::hv3::ReturnWithArgs 0] configure-isvisitedcmd $me $O(myHv3) Subscribe onclick [list $me handle_onclick] } proc reset {me} { upvar $me O set O(myLinkHoverCount) 0 } # This is the configure method for the -isvisitedcmd option. This # option configures a callback script that sets or clears the 'visited' # and 'link' properties of an element. This is a # performance critical operation because it is called so many times. # proc configure-isvisitedcmd {me} { upvar $me O # Create a proc to use as the node-handler for elements. # set P_NODE ${me}.a_node_handler catch {rename $P_NODE ""} set template [list \ proc $P_NODE {node} { if {![catch { set uri [%BASEURI% resolve [$node attr href]] }]} { if {[%VISITEDCMD% $uri]} { $node dynamic set visited } else { $node dynamic set link } } } ] eval [::hv3::Expand $template \ %BASEURI% $O(myBaseUri) %VISITEDCMD% $O(-isvisitedcmd) ] # Create a proc to use as the attribute-handler for elements. # set P_ATTR ${me}.a_attr_handler catch {rename $P_ATTR ""} set template [list \ proc $P_ATTR {node attr val} { if {$attr eq "href"} { if {![catch { set uri [%BASEURI% resolve $val] }]} { if {[%VISITEDCMD% $uri]} { $node dynamic set visited } else { $node dynamic set link } } } } ] eval [::hv3::Expand $template \ %BASEURI% $O(myBaseUri) %VISITEDCMD% $O(-isvisitedcmd) ] $O(myHv3) html handler node a $P_NODE $O(myHv3) html handler attribute a $P_ATTR } # This method is called whenever an onclick event occurs. If the # node is an with an "href" attribute that is not "#" or the # empty string, call the [goto] method of some hv3 widget to follow # the hyperlink. # # The particular hv3 widget is located by evaluating the -targetcmd # callback script. This allows the upper layer to implement frames, # links that open in new windows/tabs - all that irritating stuff :) # proc handle_onclick {me node} { upvar $me O if {[$node tag] eq "a"} { set href [$node attr -default "" href] if {$href ne "" && $href ne "#"} { set hv3 [eval [linsert $O(-targetcmd) end $node]] set href [$O(myBaseUri) resolve $href] after idle [list $hv3 goto $href -referer [$O(myHv3) location]] } } } proc destroy {me} { catch {rename ${me}.a_node_handler ""} catch {rename ${me}.a_attr_handler ""} } } ::hv3::make_constructor ::hv3::hv3::hyperlinkmanager # # End of ::hv3::hv3::hyperlinkmanager #-------------------------------------------------------------------------- namespace eval ::hv3::hv3::framelog { proc new {me hv3} { upvar $me O set O(myHv3) $hv3 set O(myStyleErrors) {} set O(myHtmlDocument) {} } proc destroy {me} { uplevel #0 [list unset $me] rename $me "" } proc loghtml {me data} { upvar $me O if {![info exists ::hv3::log_source_option]} return if {$::hv3::log_source_option} { append O(myHtmlDocument) $data } } proc log {me id filename data parse_errors} { upvar $me O if {![info exists ::hv3::log_source_option]} return if {$::hv3::log_source_option} { lappend O(myStyleErrors) [list $id $filename $data $parse_errors] } } proc clear {me} { upvar $me O set O(myStyleErrors) "" set O(myHtmlDocument) "" } proc get {me args} { upvar $me O switch -- [lindex $args 0] { html { return $O(myHtmlDocument) } css { return $O(myStyleErrors) } } } } ::hv3::make_constructor ::hv3::hv3::framelog #-------------------------------------------------------------------------- # Class hv3 - the public widget class. # namespace eval ::hv3::hv3 { proc theselectionmanager {me args} { upvar #0 $me O eval $O(mySelectionManager) $args } proc log {me args} { upvar #0 $me O eval $O(myFrameLog) $args } proc uri {me args} { upvar #0 $me O eval $O(myUri) $args } proc Subscribe {me args} { upvar #0 $me O eval $O(myMouseManager) subscribe $args } proc selected {me args} { upvar #0 $me O eval $O(mySelectionManager) selected $args } set TextWrapper {
  }

  proc new {me args} {
    upvar #0 $me O
    set win $O(win)
	   
    # The scrolled html widget.
    # set O(myHtml) [::hv3::scrolled html $win.html]
    set O(myHtml) $O(hull)
    set O(html) [html $me]
    catch {::hv3::profile::instrument [$O(myHtml) widget]}

    # Current location and base URIs. The default URI is "blank://".
    set O(myUri)  [::tkhtml::uri home://blank/]
    set O(myBase) [::tkhtml::uri home://blank/]

    # Component objects.
    set O(myMouseManager)     [mousemanager       %AUTO% $me]
    set O(myHyperlinkManager) [hyperlinkmanager   %AUTO% $me $O(myBase)]
    set O(mySelectionManager) [selectionmanager   %AUTO% $me]
    set O(myDynamicManager)   [dynamicmanager     %AUTO% $me]
    set O(myFormManager)      [::hv3::formmanager %AUTO% $me]
    set O(myFrameLog)         [framelog           %AUTO% $me]

    set O(-storevisitedcmd) ""

    set O(myStorevisitedDone) 0
    set O(-historydoccmd) ""

    # The option to display images (default true).
    set O(-enableimages) 1

    # The option to execute javascript (default false). 
    #
    # When javascript is enabled, the O(myDom) variable is set to the name of
    # an object of type [::hv3::dom]. When it is not enabled, O(myDom) is
    # an empty string.
    #
    # When the -enablejavascript option is changed from true to false,
    # the O(myDom) object is deleted (and O(myDom) set to the empty 
    # string). But the dom object is not created immediately when 
    # -enablejavascript is changed from false to true. Instead, we
    # wait until the next time the hv3 widget is reset.
    #
    set O(-enablejavascript) 0
    set O(myDom) ""

    set O(-scrollbarpolicy) auto

    set O(-locationvar) ""
    set O(-downloadcmd) ""
    set O(-requestcmd) ""

    set O(-frame) ""

    # Full text of referrer URI, if any.
    #
    # Note that the DOM attribute HTMLDocument.referrer has a double-r,
    # but the name of the HTTP header, "Referer", has only one.
    #
    set O(myReferrer) ""
  
    # Used to assign internal stylesheet ids.
    set O(myStyleCount) 0
  
    # This variable may be set to "unknown", "quirks" or "standards".
    set O(myQuirksmode) unknown
  
    set O(myFirstReset) 1
  
    # Current value to set the -cachecontrol option of download handles to.
    #
    set O(myCacheControl) normal
  
    # This variable stores the current type of resource being displayed.
    # When valid, it is set to one of the following:
    #
    #     * html
    #     * image
    #
    # Otherwise, it is set to an empty string, indicating that the resource
    # has been requested, but has not yet arrived.
    #
    set O(myMimetype) ""
  
    # This variable is only used when ($O(myMimetype) eq "image"). It stores
    # the data for the image about to be displayed. Once the image
    # has finished downloading, the data in this variable is loaded into
    # a Tk image and this variable reset to "".
    #
    set O(myImageData) ""
  
    # If this variable is not set to the empty string, it is the id of an
    # [after] event that will refresh the current document (i.e from a 
    # Refresh header or  markup). This scheduled 
    # event should be cancelled when the [reset] method is called.
    #
    # There should only be one Refresh event scheduled at any one time.
    # The [Refresh] method, which calls [after] to schedule the events,
    # cancels any pending event before scheduling a new one.
    #
    set O(myRefreshEventId) ""
  
    # This boolean variable is set to zero until the first call to [goto].
    # Before that point it is safe to change the values of the -enableimages
    # option without reloading the document.
    #
    set O(myGotoCalled) 0
  
    # This boolean variable is set after the DOM "onload" event is fired.
    # It is cleared by the [reset] method.
    set O(myOnloadFired) 0
  
    set O(myFragmentSeek) ""
  
    # The ::hv3::request object used to retrieve the main document.
    #
    set O(myDocumentHandle) ""
  
    # List of handle objects that should be released after the page has
    # loaded. This is part of the hack to work around the polipo bug.
    #
    set O(myShelvedHandles) [list]
  
    # List of all active download handles.
    #
    set O(myActiveHandles) [list]
  
    set O(myTitleVar) ""

    $O(myMouseManager) subscribe motion [list $O(mySelectionManager) motion]

    $O(myFormManager) configure -getcmd  [list $me Formcmd get]
    $O(myFormManager) configure -postcmd [list $me Formcmd post]

    # Attach an image callback to the html widget. Store images as 
    # pixmaps only when possible to save memory.
    $O(myHtml) configure -imagecmd [list $me Imagecmd] -imagepixmapify 1

    # Register node handlers to deal with the various elements
    # that may appear in the document . In html, the  section
    # may contain the following elements:
    #
    #