dom.tcl

Go to the documentation of this file.
00001 /*  dom.tcl --*/
00002 /* */
00003 /*  This file implements the Tcl language binding for the DOM -*/
00004 /*  the Document Object Model.  Support for the core specification*/
00005 /*  is given here.  Layered support for specific languages, */
00006 /*  such as HTML, will be in separate modules.*/
00007 /* */
00008 /*  Copyright (c) 1998-2004 Zveno Pty Ltd*/
00009 /*  http://www.zveno.com/*/
00010 /* */
00011 /*  See the file "LICENSE" in this distribution for information on usage and*/
00012 /*  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00013 /* */
00014 /*  $Id: dom.tcl,v 1.23 2004/02/25 20:10:28 balls Exp $*/
00015 
00016 /*  We need the xml package, so that we get Name defined*/
00017 
00018 package require xml 3.0
00019 
00020 package provide dom::tcl 3.0
00021 
00022 /*  Define generic constants*/
00023 
00024 namespace dom {
00025     namespace export DOMImplementation
00026     namespace export hasFeature createDocument create createDocumentType
00027     namespace export createNode destroy isNode parse selectNode serialize
00028     namespace export trim
00029 
00030     namespace export document documentFragment node
00031     namespace export element textNode attribute
00032     namespace export ret essingInstruction
00033     namespace export documenttype
00034     namespace export event
00035 
00036     variable maxSpecials
00037     if (![type info , type exists , type maxSpecials]) {
00038     set maxSpecials 10
00039     }
00040 
00041     variable strictDOM 0
00042 
00043     /*  Default -indentspec value*/
00044     /*  spaces-per-indent-level {collapse-re collapse-value}*/
00045     variable indentspec [list 2 [list {        } \t]]
00046 
00047     /*  The Namespace URI for XML Namespace declarations*/
00048     variable xmlnsURI http://www.w3.org/2000/xmlns/
00049 
00050     /*  DOM Level 2 Event defaults*/
00051     variable bubbles
00052     array  bubbles =  {
00053     DOMFocusIn 1
00054     DOMFocusOut 1
00055     DOMActivate 1
00056     click 1
00057     mousedown 1
00058     mouseup 1
00059     mouseover 1
00060     mousemove 1
00061     mouseout 1
00062     DOMSubtreeModified 1
00063     DOMNodeInserted 1
00064     DOMNodeRemoved 1
00065     DOMNodeInsertedIntoDocument 0
00066     DOMNodeRemovedFromDocument 0
00067     DOMAttrModified 1
00068     DOMAttrRemoved 1
00069     DOMCharacterDataModified 1
00070     }
00071     variable cancelable
00072     array  cancelable =  {
00073     DOMFocusIn 0
00074     DOMFocusOut 0
00075     DOMActivate 1
00076     click 1
00077     mousedown 1
00078     mouseup 1
00079     mouseover 1
00080     mousemove 0
00081     mouseout 1
00082     DOMSubtreeModified 0
00083     DOMNodeInserted 0
00084     DOMNodeRemoved 0
00085     DOMNodeInsertedIntoDocument 0
00086     DOMNodeRemovedFromDocument 0
00087     DOMAttrModified 0
00088     DOMAttrRemoved 0
00089     DOMCharacterDataModified 0
00090     }
00091 }
00092 
00093 namespace dom::tcl {
00094     namespace export DOMImplementation
00095     namespace export hasFeature createDocument create createDocumentType
00096     namespace export createNode destroy isNode parse selectNode serialize
00097     namespace export trim
00098 
00099     namespace export document documentFragment node
00100     namespace export element textNode attribute
00101     namespace export ret essingInstruction
00102     namespace export event
00103 }
00104 
00105 foreach p (type DOMImplementation , type hasFeature , type createDocument , type create , type createDocumentType , type createNode , type destroy , type isNode , type parse , type selectNode , type serialize , type trim , type document , type documentFragment , type node , type element , type textNode , type attribute , type processingInstruction , type event , type documenttype) {
00106 
00107     proc dom::$p args "return \[eval tcl::$p \$args\]"
00108 
00109 }
00110 
00111 /*  Data structures*/
00112 /* */
00113 /*  Documents are stored in a Tcl namespace within the ::dom namespace.*/
00114 /*  The Document array variable stores data for the document itself.*/
00115 /*  Each node has an array variable for its data.*/
00116 /* */
00117 /*  "Live" data objects are stored as a separate Tcl variable.*/
00118 /*  Lists, such as child node lists, are Tcl list variables (ie scalar)*/
00119 /*  and keyed-value lists, such as attribute lists, are Tcl array*/
00120 /*  variables.  The accessor function returns the variable name,*/
00121 /*  which the application should treat as a read-only object.*/
00122 /* */
00123 /*  A token is a FQ Tcl variable name.*/
00124 
00125 /*  dom::tcl::DOMImplementation --*/
00126 /* */
00127 /*  Implementation-dependent functions.*/
00128 /*  Most importantly, this command provides a function to*/
00129 /*  create a document instance.*/
00130 /* */
00131 /*  Arguments:*/
00132 /*  method  method to invoke*/
00133 /*  token   token for node*/
00134 /*  args    arguments for method*/
00135 /* */
00136 /*  Results:*/
00137 /*  Depends on method used.*/
00138 
00139 namespace dom::tcl {
00140     variable DOMImplementationOptions {}
00141     variable DOMImplementationCounter
00142     if {![info exists DOMImplementationCounter]} {
00143      DOMImplementationCounter =  0
00144     }
00145 }
00146 
00147 ret  dom::tcl::DOMImplementation (type method , type args) {
00148     variable DOMImplementationOptions
00149     variable DOMImplementationCounter
00150 
00151     switch -- $method {
00152 
00153     hasFeature {
00154 
00155         if {[llength $args] != 2} {
00156         return -code error "wrong # args: should be dom::DOMImplementation method args..."
00157         }
00158 
00159         # Later on, could use Tcl package facility
00160         if {[regexp {create|destroy|parse|query|serialize|trim|Events|UIEvents|isNode} [lindex $args 0]]} {
00161         if {![string compare [lindex $args 1] "1.0"]} {
00162             return 1
00163         } else {
00164             return 0
00165         }
00166         } else {
00167         return 0
00168         }
00169 
00170     }
00171 
00172     createDocument {
00173         # createDocument introduced in DOM Level 2
00174 
00175         if {[llength $args] != 3} {
00176         return -code error "wrong # args: should be DOMImplementation nsURI name doctype"
00177         }
00178 
00179         set doc [DOMImplementation create]
00180 
00181         if {[string length [lindex $args 2]]} {
00182         array set $doc [list document:doctype [lindex $args 2]]
00183         }
00184 
00185         document createElementNS $doc [lindex $args 0] [lindex $args 1]
00186 
00187         return $doc
00188     }
00189 
00190     create {
00191 
00192         # Non-standard method (see createDocument)
00193         # Bootstrap a document instance
00194 
00195         if {[llength $args] > 0} {
00196         return -code error "wrong # args: should be DOMImplementation create"
00197         }
00198 
00199         # Allocate unique document array name
00200         set ns [namespace current]::document[incr DOMImplementationCounter]
00201         set name ${ns}::Document
00202 
00203         # Create the Tcl namespace for this document
00204         namespace eval $ns {
00205         namespace export Document
00206         }
00207 
00208         set varPrefix ${name}var
00209         set arrayPrefix ${name}arr
00210 
00211         array set $name [list counter 1 \
00212         node:nodeType document          \
00213         node:parentNode {}          \
00214         node:nodeName #document         \
00215         node:nodeValue {}           \
00216         node:childNodes ${varPrefix}1       \
00217         documentFragment:masterDoc $name    \
00218         document:implementation [namespace current]::DOMImplementation      \
00219         document:xmldecl {version 1.0}      \
00220         document:documentElement {}     \
00221         document:doctype {}         \
00222         ]
00223 
00224         # Initialise child node list
00225         set $varPrefix {}
00226 
00227         # Create a Tcl command for the document
00228         proc $name {method args} "return \[eval [namespace current]::document \[list \$method\] $name \$args\]"
00229 
00230         # Capture destruction of the document
00231         trace add command $name delete [namespace code [list Document:Delete $name]]
00232 
00233         # Return the new toplevel node
00234         return $name
00235     }
00236 
00237     createDocumentType {
00238         # Introduced in DOM Level 2
00239 
00240         # Patch from c.l.t., Richard Calmbach (rc@hnc.com )
00241 
00242         if {[llength $args] < 3 || [llength $args] > 4} {
00243         return -code error "wrong # args: should be: DOMImplementation createDocumentType qname publicid systemid ?internaldtd?"
00244         }
00245 
00246         return [eval CreateDocType $args]
00247     }
00248 
00249     createNode {
00250         # Non-standard method
00251         # Creates node(s) in the given document given an XPath expression
00252 
00253         if {[llength $args] != 2} {
00254         return -code error "wrong # args: should be dom::DOMImplementation createNode xpath"
00255         }
00256 
00257         package require xpath
00258 
00259         return [XPath:CreateNode [lindex $args 0] [lindex $args 1]]
00260     }
00261 
00262     destroy {
00263 
00264         # Free all memory associated with a node
00265 
00266         if {[llength $args] != 1} {
00267         return -code error "wrong # args: should be dom::DOMImplementation destroy token"
00268         }
00269 
00270         if {[catch {upvar #0 [lindex $args 0] node}]} {
00271         # If the document is being destroyed then the Tcl namespace no longer exists
00272         return {}
00273         }
00274 
00275         switch $node(node:nodeType) {
00276 
00277         document -
00278         documentFragment {
00279 
00280             if {[string length $node(node:parentNode)]} {
00281             unset $node(node:childNodes)
00282 
00283             # Dispatch events
00284             event postMutationEvent $node(node:parentNode) DOMSubtreeModified
00285 
00286             return {}
00287             }
00288 
00289             # else this is the root document node,
00290             # and we can optimize the cleanup.
00291             # No need to dispatch events.
00292 
00293             # First remove all command traces
00294             foreach nodecmd [info commands [namespace qualifiers [lindex $args 0]]::*] {
00295             trace remove command $nodecmd delete [namespace code [list Node:Delete $nodecmd]]
00296             }
00297 
00298             namespace delete [namespace qualifiers [lindex $args 0]]
00299         }
00300 
00301         documentType {
00302             trace remove command [lindex $args 0] delete [namespace code [list DocumentType:Delete [lindex $args 0]]]
00303             rename [lindex $args 0] {}
00304             unset [lindex $args 0]
00305         }
00306 
00307         element {
00308             # First make sure the node is removed from the tree
00309             if {[string length $node(node:parentNode)]} {
00310             node removeChild $node(node:parentNode) [lindex $args 0]
00311             }
00312             unset $node(node:childNodes)
00313             unset $node(element:attributeList)
00314             unset node
00315             set name [lindex $args 0]
00316             trace remove command $name delete [namespace code [list Node:Delete $name]]
00317             rename $name {}
00318 
00319             # Don't dispatch events here -
00320             # already done by removeChild
00321         }
00322 
00323         event {
00324             set name [lindex $args 0]
00325             trace remove command $name delete [namespace code [list Node:Delete $name]]
00326             rename $name {}
00327             unset node
00328         }
00329 
00330         default {
00331             # Store the parent for later
00332             set parent $node(node:parentNode)
00333 
00334             # First make sure the node is removed from the tree
00335             if {[string length $node(node:parentNode)]} {
00336             node removeChild $node(node:parentNode) [lindex $args 0]
00337             }
00338             unset node
00339             set name [lindex $args 0]
00340             trace remove command $name delete [namespace code [list Node:Delete $name]]
00341             rename $name {}
00342 
00343             # Dispatch events
00344             event postMutationEvent $parent DOMSubtreeModified
00345 
00346         }
00347 
00348         }
00349 
00350         return {}
00351 
00352     }
00353 
00354     isNode {
00355         # isNode - non-standard method
00356         # Sometimes it is useful to check if an arbitrary string
00357         # refers to a DOM node
00358 
00359         upvar #0 [lindex $args 0] node
00360 
00361         if {![info exists node]} {
00362         return 0
00363         } elseif {[info exists node(node:nodeType)]} {
00364         return 1
00365         } else {
00366         return 0
00367         }
00368     }
00369 
00370     parse {
00371 
00372         # This implementation uses TclXML version 2.0.
00373         # TclXML can choose the best installed parser.
00374 
00375         if {[llength $args] < 1} {
00376         return -code error "wrong # args: should be dom::DOMImplementation parse xml ?args...?"
00377         }
00378 
00379         array set opts {-parser {} -progresscommand {} -chunksize 8196}
00380         if {[catch {array set opts [lrange $args 1 end]}]} {
00381         return -code error "bad configuration options"
00382         }
00383 
00384         # Create a state array for this parse session
00385         set state [namespace current]::parse[incr DOMImplementationCounter]
00386         array set $state [array get opts -*]
00387         array set $state [list progCounter 0]
00388         set errorCleanup {}
00389 
00390         if {[string length $opts(-parser)]} {
00391         set parserOpt [list -parser $opts(-parser)]
00392         } else {
00393         set parserOpt {}
00394         }
00395         if {[catch {package require xml} version]} {
00396         eval $errorCleanup
00397         return -code error "unable to load XML parsing package"
00398         }
00399         set parser [eval xml::parser $parserOpt]
00400 
00401         $parser configure \
00402         -elementstartcommand [namespace code [list ParseElementStart $state]]   \
00403         -elementendcommand [namespace code [list ParseElementEnd $state]]   \
00404         -characterdatacommand [namespace code [list ParseCharacterData $state]] \
00405         -processinginstructioncommand [namespace code [list ParseProcessingInstruction $state]] \
00406         -commentcommand [namespace code [list ParseComment $state]] \
00407         -entityreferencecommand [namespace code [list ParseEntityReference $state]] \
00408         -xmldeclcommand [namespace code [list ParseXMLDeclaration $state]] \
00409         -doctypecommand [namespace code [list ParseDocType $state]] \
00410         -final 1
00411 
00412         # Create top-level document
00413         array set $state [list docNode [DOMImplementation create]]
00414         array set $state [list current [lindex [array get $state docNode] 1]]
00415 
00416         # Parse data
00417         # Bug in TclExpat - doesn't handle non-final inputs
00418         if {0 && [string length $opts(-progresscommand)]} {
00419         $parser configure -final false
00420         while {[string length [lindex $args 0]]} {
00421             $parser parse [string range [lindex $args 0] 0 $opts(-chunksize)]
00422             set args [lreplace $args 0 0 \
00423             [string range [lindex $args 0] $opts(-chunksize) end]]
00424             uplevel #0 $opts(-progresscommand)
00425         }
00426         $parser configure -final true
00427         } elseif {[catch {$parser parse [lindex $args 0]} err]} {
00428         catch {rename $parser {}}
00429         catch {unset $state}
00430         return -code error $err
00431         }
00432 
00433         # Free data structures which are no longer required
00434         $parser free
00435         catch {rename $parser {}}
00436 
00437         set doc [lindex [array get $state docNode] 1]
00438         unset $state
00439         return $doc
00440 
00441     }
00442 
00443     selectNode {
00444         # Non-standard method
00445         # Returns nodeset in the given document matching an XPath expression
00446 
00447         if {[llength $args] != 2} {
00448         return -code error "wrong # args: should be dom::DOMImplementation selectNode token xpath"
00449         }
00450 
00451         package require xpath
00452 
00453         return [XPath:SelectNode [lindex $args 0] [lindex $args 1]]
00454     }
00455 
00456     serialize {
00457 
00458         if {[llength $args] < 1} {
00459         return -code error "wrong # args: should be dom::DOMImplementation serialize token"
00460         }
00461 
00462         upvar #0 [lindex $args 0] node
00463 
00464         return [eval [list Serialize:$node(node:nodeType)] $args]
00465 
00466     }
00467 
00468     trim {
00469 
00470         # Removes textNodes that only contain white space
00471 
00472         if {[llength $args] != 1} {
00473         return -code error "wrong # args: should be dom::DOMImplementation trim token"
00474         }
00475 
00476         Trim [lindex $args 0]
00477 
00478         # Dispatch DOMSubtreeModified event once here?
00479 
00480         return {}
00481 
00482     }
00483 
00484     default {
00485         return -code error "unknown method \"$method\""
00486     }
00487 
00488     }
00489 
00490     return {}
00491 }
00492 
00493 namespace dom::tcl {
00494     foreach ret  {hasFeature createDocument create createDocumentType createNode destroy isNode parse selectNode serialize trim} (
00495     type proc $, type method , type args ", type eval [, type namespace , type current]::, type DOMImplementation $, type method \$, type args"
00496     )
00497 }
00498 
00499 # dom::tcl::Document:Delete --
00500 #
00501 #   Handle destruction of a document
00502 #
00503 # Arguments:
00504 #   name    document token
00505 #   old )
00506 #   new ) args added by trace command
00507 #   op  )
00508 
00509 proc dom::tcl::Document:Delete {name old new op} {
00510     DOMImplementation destroy $name
00511     return {}
00512 }
00513 
00514 /*  dom::tcl::document --*/
00515 /* */
00516 /*  Functions for a document node.*/
00517 /* */
00518 /*  Arguments:*/
00519 /*  method  method to invoke*/
00520 /*  token   token for node*/
00521 /*  args    arguments for method*/
00522 /* */
00523 /*  Results:*/
00524 /*  Depends on method used.*/
00525 
00526 namespace dom::tcl {
00527     variable documentOptionsRO doctype|implementation|documentElement
00528     variable documentOptionsRW actualEncoding|encoding|standalone|version
00529 }
00530 
00531 ret  dom::tcl::document (type method , type token , type args) {
00532     variable documentOptionsRO
00533     variable documentOptionsRW
00534 
00535     upvar #0 $token node
00536 
00537     set result {}
00538 
00539     switch -- $method {
00540     cget {
00541         if {[llength $args] != 1} {
00542         return -code error "wrong # args: should be \"dom::document method token ?args ...?\""
00543         }
00544         if {[regexp [format {^-(%s)$} $documentOptionsRO] [lindex $args 0] discard option]} {
00545         return $node(document:$option)
00546         } elseif {[regexp [format {^-(%s)$} $documentOptionsRW] [lindex $args 0] discard option]} {
00547         switch -- $option {
00548             encoding -
00549             version -
00550             standalone {
00551             array set xmldecl $node(document:xmldecl)
00552             return $xmldecl($option)
00553             }
00554             default {
00555             return $node(document:$option)
00556             }
00557         }
00558         } else {
00559         return -code error "bad option \"[lindex $args 0]\""
00560         }
00561     }
00562     configure {
00563         if {[llength $args] == 1} {
00564         return [document cget $token [lindex $args 0]]
00565         } elseif {[expr [llength $args] % 2]} {
00566         return -code error "no value specified for option \"[lindex $args end]\""
00567         } else {
00568         foreach {option value} $args {
00569             if {[regexp [format {^-(%s)$} $documentOptionsRW] $option discard opt]} {
00570             switch -- $opt {
00571                 encoding {
00572                 catch {unset xmldecl}
00573                 array set xmldecl $node(document:xmldecl)
00574                 set xmldecl(encoding) $value
00575                 set node(document:xmldecl) [array get xmldecl]
00576                 }
00577                 standalone {
00578                 if {[string is boolean $value]} {
00579                     catch {unset xmldecl}
00580                     array set xmldecl $node(document:xmldecl)
00581                     if {[string is true $value]} {
00582                     set xmldecl(standalone) yes
00583                     } else {
00584                     set xmldecl(standalone) no
00585                     }
00586                     set node(document:xmldecl) [array get xmldecl]
00587                 } else {
00588                     return -code error "unsupported value for option \"$option\" - must be boolean"
00589                 }
00590                 }
00591                 version {
00592                 if {$value == "1.0"} {
00593                     catch {unset xmldecl}
00594                     array set xmldecl $node(document:xmldecl)
00595                     set xmldecl(version) $value
00596                     set node(document:xmldecl) [array get xmldecl]
00597                 } else {
00598                     return -code error "unsupported value for option \"$option\""
00599                 }
00600                 }
00601                 default {
00602                 set node(document:$opt) $value
00603                 }
00604             }
00605             } elseif {[regexp [format {^-(%s)$} $documentOptionsRO] $option discard opt]} {
00606             return -code error "attribute \"$option\" is read-only"
00607             } else {
00608             return -code error "bad option \"$option\""
00609             }
00610         }
00611         }
00612     }
00613 
00614     createElement {
00615         if {[llength $args] != 1} {
00616         return -code error "wrong # args: should be \"document createElement token name\""
00617         }
00618 
00619         # Check that the element name is kosher
00620         if {![regexp ^$::xml::Name\$ [lindex $args 0]]} {
00621         return -code error "invalid element name \"[lindex $args 0]\""
00622         }
00623 
00624         # Invoke internal factory function
00625         set result [CreateElement $token [lindex $args 0] {}]
00626 
00627     }
00628     createDocumentFragment {
00629         if {[llength $args]} {
00630         return -code error "wrong # args: should be \"document createDocumentFragment token\""
00631         }
00632 
00633         set result [CreateGeneric $token node:nodeType documentFragment node:nodeName #document-fragment node:nodeValue {}]
00634     }
00635     createTextNode {
00636         if {[llength $args] != 1} {
00637         return -code error "wrong # args: should be \"document createTextNode token text\""
00638         }
00639 
00640         set result [CreateTextNode $token [lindex $args 0]]
00641     }
00642     createComment {
00643         if {[llength $args] != 1} {
00644         return -code error "wrong # args: should be \"document createComment token data\""
00645         }
00646 
00647         set result [CreateGeneric $token node:nodeType comment node:nodeName #comment node:nodeValue [lindex $args 0]]
00648     }
00649     createCDATASection {
00650         if {[llength $args] != 1} {
00651         return -code error "wrong # args: should be \"document createCDATASection token data\""
00652         }
00653 
00654         set result [CreateTextNode $token [lindex $args 0]]
00655         node configure $result -cdatasection 1
00656     }
00657     createProcessingInstruction {
00658         if {[llength $args] != 2} {
00659         return -code error "wrong # args: should be \"document createProcessingInstruction token target data\""
00660         }
00661 
00662         set result [CreateGeneric $token node:nodeType processingInstruction \
00663             node:nodeName [lindex $args 0] node:nodeValue [lindex $args 1]]
00664     }
00665     createAttribute {
00666         if {[llength $args] != 1} {
00667         return -code error "wrong # args: should be \"document createAttributes token name\""
00668         }
00669 
00670         # Check that the attribute name is kosher
00671         if {![regexp ^$::xml::Name\$ [lindex $args 0]]} {
00672         return -code error "invalid attribute name \"[lindex $args 0]\""
00673         }
00674 
00675         set result [CreateGeneric $token node:nodeType attribute node:nodeName [lindex $args 0]]
00676     }
00677     createEntity {
00678         set result [CreateGeneric $token node:nodeType entity]
00679     }
00680     createEntityReference {
00681         if {[llength $args] != 1} {
00682         return -code error "wrong # args: should be \"document createEntityReference token name\""
00683         }
00684         set result [CreateGeneric $token node:nodeType entityReference node:nodeName [lindex $args 0]]
00685     }
00686 
00687     importNode {
00688         # Introduced in DOM Level 2
00689 
00690         if {[llength $args] < 1} {
00691         return -code error "wrong # args: should be \"importNode token ?-deep boolean?\""
00692         }
00693         array set opts {
00694         -deep 1
00695         }
00696         array set opts [lrange $args 1 end]
00697         set opts(-deep) [Boolean $opts(-deep)]
00698 
00699         if {[namespace qualifiers [lindex $args 0]] == [namespace qualifiers $token]} {
00700         return -code error "source node \"[lindex $args 0]\" is in the same document"
00701         }
00702 
00703         switch [node cget [lindex $args 0] -nodeType] {
00704         document -
00705         documentType {
00706             return -code error "node type \"[node cget [lindex $args 0] -type]\" cannot be imported"
00707         }
00708         documentFragment {
00709             set result [document createDocumentFragment $token]
00710             if {$opts(-deep)} {
00711             foreach child [node children [lindex $args 0]] {
00712                 $result appendChild [$token importNode $child -deep 1]
00713             }
00714             }
00715         }
00716         element {
00717             set result [CreateElement {} [node cget [lindex $args 0] -nodeName] [array get [node cget [lindex $args 0] -attributes]] -document $token]
00718             if {$opts(-deep)} {
00719             foreach child [node children [lindex $args 0]] {
00720                 $result appendChild [$token importNode $child -deep 1]
00721             }
00722             }
00723         }
00724         textNode {
00725             set result [CreateTextNode {} [node cget [lindex $args 0] -nodeValue] -document $token]
00726         }
00727         attribute -
00728         processingInstruction -
00729         comment {
00730             set result [CreateGeneric {} -document $token node:nodeType [node cget [lindex $args 0] -nodeType] node:nodeName [node cget [lindex $args 0] -nodeName] node:nodeValue [node cget [lindex $args 0] -nodeValue]]
00731         }
00732         }
00733     }
00734 
00735     createElementNS {
00736         # Introduced in DOM Level 2
00737 
00738         if {[llength $args] != 2} {
00739         return -code error "wrong # args: should be: \"createElementNS nsuri qualname\""
00740         }
00741 
00742         # Check that the qualified name is kosher
00743         if {[catch {foreach {prefix localname} [::xml::qnamesplit [lindex $args 1]]  break} err]} {
00744         return -code error "invalid qualified name \"[lindex $args 1]\" due to \"$err\""
00745         }
00746 
00747         # Invoke internal factory function
00748         set result [CreateElement $token [lindex $args 1] {} -prefix $prefix -namespace [lindex $args 0] -localname $localname]
00749     }
00750 
00751     createAttributeNS {
00752         # Introduced in DOM Level 2
00753 
00754         return -code error "not yet implemented"
00755     }
00756 
00757     getElementsByTagNameNS {
00758         # Introduced in DOM Level 2
00759 
00760         return -code error "not yet implemented"
00761     }
00762 
00763     getElementsById {
00764         # Introduced in DOM Level 2
00765 
00766         return -code error "not yet implemented"
00767     }
00768 
00769     createEvent {
00770         # Introduced in DOM Level 2
00771 
00772         if {[llength $args] != 1} {
00773         return -code error "wrong # args: should be \"document createEvent token type\""
00774         }
00775 
00776         set result [CreateEvent $token [lindex $args 0]]
00777 
00778     }
00779 
00780     getElementsByTagName {
00781         if {[llength $args] < 1} {
00782         return -code error "wrong # args: should be \"document getElementsByTagName token what\""
00783         }
00784 
00785         return [eval Element:GetByTagName [list $token [lindex $args 0]] \
00786             [lrange $args 1 end]]
00787     }
00788 
00789     default {
00790         return -code error "unknown method \"$method\""
00791     }
00792 
00793     }
00794 
00795     # Dispatch events
00796 
00797     # Node insertion events are generated here instead of the
00798     # internal factory procedures.  This is because the factory
00799     # procedures are meant to be mean-and-lean during the parsing
00800     # phase, and dispatching events at that time would be an
00801     # excessive overhead.  The factory methods here are pretty
00802     # heavyweight anyway.
00803 
00804     if {[string match create* $method] && [string compare $method "createEvent"]} {
00805 
00806     event postMutationEvent $result DOMNodeInserted -relatedNode $token
00807     event postMutationEvent $result DOMNodeInsertedIntoDocument
00808     event postMutationEvent $token DOMSubtreeModified
00809 
00810     }
00811 
00812     return $result
00813 }
00814 
00815 /*  Factory methods*/
00816 /* */
00817 /*  These are lean-and-mean for fastest possible tree building*/
00818 
00819 /*  dom::tcl::CreateElement --*/
00820 /* */
00821 /*  Append an element to the given (parent) node (if any)*/
00822 /* */
00823 /*  Arguments:*/
00824 /*  token   parent node (if empty -document option is mandatory)*/
00825 /*  name    element name (no checking performed here)*/
00826 /*  aList   attribute list*/
00827 /*  args    configuration options*/
00828 /* */
00829 /*  Results:*/
00830 /*  New node created, parent optionally modified*/
00831 
00832 ret  dom::tcl::CreateElement (type token , type name , type aList , type args) {
00833     array set opts $args
00834 
00835     if {[string length $token]} {
00836     upvar #0 $token parent
00837     upvar #0 [namespace qualifiers $token]::Document document
00838     set child [namespace qualifiers $token]::node[incr document(counter)]
00839     } elseif {[info exists opts(-document)]} {
00840     upvar #0 $opts(-document) document
00841     set child [namespace qualifiers $opts(-document)]::node[incr document(counter)]
00842     } else {
00843     return -code error "no parent or document specified"
00844     }
00845 
00846     upvar #0 $child new
00847 
00848     # Create the new node
00849     # NB. normally we'd use Node:create here,
00850     # but inline it instead for performance
00851     array set new [list \
00852         node:parentNode $token      \
00853         node:childNodes ${child}var     \
00854         node:nodeType element       \
00855         node:nodeName $name         \
00856         node:namespaceURI {}        \
00857         node:prefix {}          \
00858         node:localName $name        \
00859         node:nodeValue {}           \
00860         element:attributeList ${child}arr   \
00861         element:attributeNodes {}       \
00862     ]
00863 
00864     catch {set new(node:namespaceURI) $opts(-namespace)}
00865     catch {set new(node:localName) $opts(-localname)}
00866     catch {set new(node:prefix) $opts(-prefix)}
00867 
00868     # Initialise associated variables
00869     set ${child}var {}
00870     array set ${child}arr $aList
00871     catch {
00872     foreach {ns nsAttrList} $opts(-namespaceattributelists) {
00873         foreach {attrName attrValue} $nsAttrList {
00874         array set ${child}arr [list $ns^$attrName $attrValue]
00875         }
00876     }
00877     }
00878 
00879     # Update parent record
00880 
00881     # Does this element qualify as the document element?
00882     # If so, then has a document element already been set?
00883 
00884     if {[string length $token] && 
00885     [string equal $parent(node:nodeType) document]} {
00886 
00887     if {$token == $parent(documentFragment:masterDoc)} {
00888         if {[info exists parent(document:documentElement)] && \
00889             [string length $parent(document:documentElement)]} {
00890         # Do not attach to the tree
00891         set new(node:parentNode) {}
00892         } else {
00893 
00894         # Check against document type decl
00895         if {[string length $parent(document:doctype)]} {
00896             upvar #0 $parent(document:doctype) doctypedecl
00897             if {[string compare $name $doctypedecl(doctype:name)]} {
00898             return -code error "mismatch between root element type in document type declaration \"$doctypedecl(doctype:name)\" and root element \"$name\""
00899             }
00900 
00901         } else {
00902             # Synthesize document type declaration
00903             set doctype [CreateDocType $name {} {}]
00904             set document(document:doctype) $doctype
00905         }
00906 
00907         set parent(document:documentElement) $child
00908         catch {lappend $parent(node:childNodes) $child}
00909         }
00910     } else {
00911         catch {lappend $parent(node:childNodes) $child}
00912     }
00913     } else {
00914     catch {lappend $parent(node:childNodes) $child}
00915     }
00916 
00917     proc $child {method args} "return \[eval [namespace current]::node \[list \$method\] $child \$args\]"
00918     trace add command $child delete [namespace code [list Node:Delete $child]]
00919 
00920     return $child
00921 }
00922 
00923 /*  dom::tcl::CreateTextNode --*/
00924 /* */
00925 /*  Append a textNode node to the given (parent) node (if any).*/
00926 /* */
00927 /*  This factory function can also be performed by*/
00928 /*  CreateGeneric, but text nodes are created so often*/
00929 /*  that this specific factory procedure speeds things up.*/
00930 /* */
00931 /*  Arguments:*/
00932 /*  token   parent node (if empty -document option is mandatory)*/
00933 /*  text    initial text*/
00934 /*  args    additional configuration options*/
00935 /* */
00936 /*  Results:*/
00937 /*  New node created, parent optionally modified*/
00938 
00939 ret  dom::tcl::CreateTextNode (type token , type text , type args) {
00940     array set opts $args
00941 
00942     if {[string length $token]} {
00943     upvar #0 $token parent
00944     upvar #0 [namespace qualifiers $token]::Document document
00945     set child [namespace qualifiers $token]::node[incr document(counter)]
00946     } elseif {[info exists opts(-document)]} {
00947     upvar #0 $opts(-document) document
00948     set child [namespace qualifiers $opts(-document)]::node[incr document(counter)]
00949     } else {
00950     return -code error "no parent or document specified"
00951     }
00952 
00953     upvar #0 $child new
00954 
00955     # Create the new node
00956     # NB. normally we'd use Node:create here,
00957     # but inline it instead for performance
00958 
00959     # Text nodes never have children, so don't create a variable
00960 
00961     array set new [list \
00962         node:parentNode $token      \
00963         node:childNodes ${child}var     \
00964         node:nodeType textNode      \
00965         node:nodeValue $text        \
00966         node:nodeName #text         \
00967         node:cdatasection 0         \
00968     ]
00969 
00970     set ${child}var {}
00971 
00972     # Update parent record
00973     catch {lappend $parent(node:childNodes) $child}
00974 
00975     proc $child {method args} "return \[eval [namespace current]::node \[list \$method\] $child \$args\]"
00976     trace add command $child delete [namespace code [list Node:Delete $child]]
00977 
00978     return $child
00979 }
00980 
00981 /*  dom::tcl::CreateGeneric --*/
00982 /* */
00983 /*  This is a template used for type-specific factory procedures*/
00984 /* */
00985 /*  Arguments:*/
00986 /*  token   parent node (if empty -document option is mandatory)*/
00987 /*  args    optional values*/
00988 /* */
00989 /*  Results:*/
00990 /*  New node created, parent modified*/
00991 
00992 ret  dom::tcl::CreateGeneric (type token , type args) {
00993     array set opts $args
00994 
00995     if {[string length $token]} {
00996     upvar #0 $token parent
00997     upvar #0 [namespace qualifiers $token]::Document document
00998     set child [namespace qualifiers $token]::node[incr document(counter)]
00999     } elseif {[info exists opts(-document)]} {
01000     upvar #0 $opts(-document) document
01001     set child [namespace qualifiers $opts(-document)]::node[incr document(counter)]
01002     } else {
01003     return -code error "no parent or document specified"
01004     }
01005     upvar #0 $child new
01006 
01007     # Create the new node
01008     # NB. normally we'd use Node:create here,
01009     # but inline it instead for performance
01010     array set new [eval list [list \
01011         node:parentNode $token  \
01012         node:childNodes ${child}var ] \
01013         $args           \
01014     ]
01015     set ${child}var {}
01016 
01017     switch -glob -- [string length $token],$opts(node:nodeType) {
01018     0,* -
01019     *,attribute -
01020     *,namespace {
01021         # These type of nodes are not children of their parent
01022     }
01023 
01024     default {
01025         # Update parent record
01026         lappend $parent(node:childNodes) $child
01027     }
01028     }
01029 
01030     proc $child {method args} "return \[eval [namespace current]::node \[list \$method\] $child \$args\]"
01031     trace add command $child delete [namespace code [list Node:Delete $child]]
01032 
01033     return $child
01034 }
01035 
01036 /*  Specials*/
01037 
01038 /*  dom::tcl::CreateDocType --*/
01039 /* */
01040 /*  Create a Document Type Declaration node.*/
01041 /* */
01042 /*  Arguments:*/
01043 /*  name    root element type*/
01044 /*  publicid    public identifier*/
01045 /*  systemid    system identifier*/
01046 /*  internaldtd internal DTD subset*/
01047 /* */
01048 /*  Results:*/
01049 /*  Returns node id of the newly created node.*/
01050 
01051 ret  dom::tcl::CreateDocType (type name , type publicid , type systemid , optional internaldtd ={)} {
01052     if {![regexp ^$::xml::QName\$ $name]} {
01053     return -code error "invalid QName \"$name\""
01054     }
01055 
01056      nodename =  [namespace current]::$name
01057     upvar /* 0 $nodename doctype*/
01058     if {[info exists doctype]} {
01059     return $nodename
01060     }
01061 
01062     if {[llength $internaldtd] == 1 && [string length [lindex $internaldtd 0]] == 0} {
01063      dtd =  {}
01064     }
01065 
01066     array  doctype =  [list \
01067         node:childNodes {} \
01068         node:nodeType documentType \
01069         node:nodeName $name \
01070         node:nodeValue {} \
01071         doctype:name $name \
01072         doctype:entities {} \
01073         doctype:notations {} \
01074         doctype:publicId $publicid \
01075         doctype:systemId $systemid \
01076         doctype:internalSub $internaldtd =  \
01077     ]
01078 
01079     ret  $nodename (type method , type args) "return \[eval [namespace current]::documenttype \[list \$method\] $nodename \$args\]"
01080     trace add command $nodename delete [namespace code [list DocumentType:Delete $nodename]]
01081 
01082     return $nodename
01083 }
01084 
01085 # dom::tcl::documenttype --
01086 #
01087 #   Functions for a document type declaration node.
01088 #
01089 # Arguments:
01090 #   method  method to invoke
01091 #   token   token for node
01092 #   args    arguments for method
01093 #
01094 # Results:
01095 #   Depends on method used.
01096 
01097 namespace eval dom::tcl {
01098     variable documenttypeOptionsRO name|entities|notations|publicId|systemId|internalSubset
01099     variable documenttypeOptionsRW {}
01100 }
01101 
01102 ret  dom::tcl::documenttype (type method , type token , type args) {
01103     variable documenttypeOptionsRO
01104     variable documenttypeOptionsRW
01105 
01106     upvar #0 $token node
01107 
01108     set result {}
01109 
01110     switch -- $method {
01111     cget {
01112         if {[llength $args] != 1} {
01113         return -code error "wrong # args: should be \"dom::documenttype method token ?args ...?\""
01114         }
01115         if {[regexp [format {^-(%s)$} $documenttypeOptionsRO] [lindex $args 0] discard option]} {
01116         switch -- $option {
01117             name {
01118             return $node(node:nodeName)
01119             }
01120             default {
01121             return $node(doctype:$option)
01122             }
01123         }
01124         } elseif {[regexp [format {^-(%s)$} $documenttypeOptionsRW] [lindex $args 0] discard option]} {
01125         return $node(doctype:$option)
01126         } else {
01127         return -code error "bad option \"[lindex $args 0]\""
01128         }
01129     }
01130     configure {
01131         if {[llength $args] == 1} {
01132         return [documenttype cget $token [lindex $args 0]]
01133         } elseif {[expr [llength $args] % 2]} {
01134         return -code error "no value specified for option \"[lindex $args end]\""
01135         } else {
01136         foreach {option value} $args {
01137             if {[regexp [format {^-(%s)$} $documenttypeOptionsRW] $option discard opt]} {
01138             switch -- $opt {
01139                 default {
01140                 set node(doctype:$opt) $value
01141                 }
01142             }
01143             } elseif {[regexp [format {^-(%s)$} $documenttypeOptionsRO] $option discard opt]} {
01144             return -code error "attribute \"$option\" is read-only"
01145             } else {
01146             return -code error "bad option \"$option\""
01147             }
01148         }
01149         }
01150     }
01151     }
01152 
01153     return $result
01154 }
01155 
01156 /*  dom::tcl::DocumentType:Delete --*/
01157 /* */
01158 /*  Handle node destruction*/
01159 /* */
01160 /*  Arguments:*/
01161 /*  name    node token*/
01162 /*  old )*/
01163 /*  new ) arguments appended by trace command*/
01164 /*  op  )*/
01165 /* */
01166 /*  Results:*/
01167 /*  Node is destroyed*/
01168 
01169 ret  dom::tcl::DocumentType:Delete (type name , type old , type new , type op) {
01170     DOMImplementation destroy $name
01171 }
01172 
01173 /*  dom::tcl::node --*/
01174 /* */
01175 /*  Functions for a general node.*/
01176 /* */
01177 /*  Implements EventTarget Interface - introduced in DOM Level 2*/
01178 /* */
01179 /*  Arguments:*/
01180 /*  method  method to invoke*/
01181 /*  token   token for node*/
01182 /*  args    arguments for method*/
01183 /* */
01184 /*  Results:*/
01185 /*  Depends on method used.*/
01186 
01187 namespace dom::tcl {
01188     variable nodeOptionsRO nodeType|parentNode|childNodes|firstChild|lastChild|previousSibling|nextSibling|attributes|namespaceURI|prefix|localName|ownerDocument
01189     variable nodeOptionsRW nodeValue|cdatasection
01190 
01191     /*  Allowing nodeName to be rw is not standard DOM.*/
01192     /*  A validating implementation would have to be very careful*/
01193     /*  in allowing this feature*/
01194     if {$::dom::strictDOM} {
01195     append nodeOptionsRO |nodeName
01196     } else {
01197     append nodeOptionsRW |nodeName
01198     }
01199 }
01200 /*  NB. cdatasection is not a standard DOM option*/
01201 
01202 ret  dom::tcl::node (type method , type token , type args) {
01203     variable nodeOptionsRO
01204     variable nodeOptionsRW
01205 
01206     upvar #0 $token node
01207 
01208     set result {}
01209 
01210     switch -glob -- $method {
01211     cg* {
01212         # cget
01213 
01214         # Some read-only configuration options are computed
01215         if {[llength $args] != 1} {
01216         return -code error "wrong # args: should be \"dom::node cget token option\""
01217         }
01218         if {[regexp [format {^-(%s)$} $nodeOptionsRO] [lindex $args 0] discard option]} {
01219         switch $option {
01220             nodeName {
01221             set result $node(node:nodeName)
01222             switch $node(node:nodeType) {
01223                 textNode {
01224                 catch {set result [expr {$node(node:cdatasection) ? "#cdata-section" : $node(node:nodeName)}]}
01225                 }
01226                 default {
01227                 }
01228             }
01229             }
01230             childNodes {
01231             # How are we going to handle documentElement?
01232             set result $node(node:childNodes)
01233             }
01234             firstChild {
01235             upvar #0 $node(node:childNodes) children
01236             switch $node(node:nodeType) {
01237                 document {
01238                 set result [lindex $children 0]
01239                 catch {set result $node(document:documentElement)}
01240                 }
01241                 default {
01242                 set result [lindex $children 0]
01243                 }
01244             }
01245             }
01246             lastChild {
01247             upvar #0 $node(node:childNodes) children
01248             switch $node(node:nodeType) {
01249                 document {
01250                 set result [lindex $children end]
01251                 catch {set result $node(document:documentElement)}
01252                 }
01253                 default {
01254                 set result [lindex $children end]
01255                 }
01256             }
01257             }
01258             previousSibling {
01259             # BUG: must take documentElement into account
01260             # Find the parent node
01261             upvar #0 $node(node:parentNode) parent
01262             upvar #0 $parent(node:childNodes) children
01263             set idx [lsearch $children $token]
01264             if {$idx >= 0} {
01265                 set sib [lindex $children [incr idx -1]]
01266                 if {[llength $sib]} {
01267                 set result $sib
01268                 } else {
01269                 set result {}
01270                 }
01271             } else {
01272                 set result {}
01273             }
01274             }
01275             nextSibling {
01276             # BUG: must take documentElement into account
01277             # Find the parent node
01278             upvar #0 $node(node:parentNode) parent
01279             upvar #0 $parent(node:childNodes) children
01280             set idx [lsearch $children $token]
01281             if {$idx >= 0} {
01282                 set sib [lindex $children [incr idx]]
01283                 if {[llength $sib]} {
01284                 set result $sib
01285                 } else {
01286                 set result {}
01287                 }
01288             } else {
01289                 set result {}
01290             }
01291             }
01292             attributes {
01293             if {[string compare $node(node:nodeType) element]} {
01294                 set result {}
01295             } else {
01296                 set result $node(element:attributeList)
01297             }
01298             }
01299             ownerDocument {
01300             if {[string compare $node(node:parentNode) {}]} {
01301                 return [namespace qualifiers $token]::Document
01302             } else {
01303                 return $token
01304             }
01305             }
01306             default {
01307             return [GetField node(node:$option)]
01308             }
01309         }
01310         } elseif {[regexp [format {^-(%s)$} $nodeOptionsRW] [lindex $args 0] discard option]} {
01311         return [GetField node(node:$option)]
01312         } else {
01313         return -code error "unknown option \"[lindex $args 0]\""
01314         }
01315     }
01316     co* {
01317         # configure
01318 
01319         if {[llength $args] == 1} {
01320         return [node cget $token [lindex $args 0]]
01321         } elseif {[expr [llength $args] % 2]} {
01322         return -code error "wrong \# args: should be \"::dom::node configure node option\""
01323         } else {
01324         foreach {option value} $args {
01325             if {[regexp [format {^-(%s)$} $nodeOptionsRW] $option discard opt]} {
01326 
01327             switch $opt,$node(node:nodeType) {
01328                 nodeValue,textNode -
01329                 nodeValue,processingInstruction {
01330                 # Dispatch event
01331                 set evid [CreateEvent $token DOMCharacterDataModified]
01332                 event initMutationEvent $evid DOMCharacterDataModified 1 0 {} $node(node:nodeValue) $value {} {}
01333                 set node(node:nodeValue) $value
01334                 node dispatchEvent $token $evid
01335                 DOMImplementation destroy $evid
01336                 }
01337                 default {
01338                 set node(node:$opt) $value
01339                 }
01340             }
01341 
01342             } elseif {[regexp [format {^-(%s)$} $nodeOptionsRO] $option discard opt]} {
01343             return -code error "attribute \"$option\" is read-only"
01344             } else {
01345             return -code error "unknown option \"$option\""
01346             }
01347         }
01348         }
01349     }
01350 
01351     in* {
01352 
01353         # insertBefore
01354 
01355         # Previous and next sibling relationships are OK, 
01356         # because they are dynamically determined
01357 
01358         if {[llength $args] < 1 || [llength $args] > 2} {
01359         return -code error "wrong # args: should be \"dom::node insertBefore token new ?ref?\""
01360         }
01361 
01362         upvar #0 [lindex $args 0] newChild
01363         if {[string compare [namespace qualifiers [lindex $args 0]] [namespace qualifiers $token]]} {
01364         return -code error "new node must be in the same document"
01365         }
01366 
01367         switch [llength $args] {
01368         1 {
01369             # Append as the last node
01370             if {[string length $newChild(node:parentNode)]} {
01371             node removeChild $newChild(node:parentNode) [lindex $args 0]
01372             }
01373             lappend $node(node:childNodes) [lindex $args 0]
01374             set newChild(node:parentNode) $token
01375         }
01376         2 {
01377             upvar #0 [lindex $args 1] refChild
01378 
01379             if {[string compare [namespace qualifiers [lindex $args 1]] [namespace qualifiers [lindex $args 0]]]} {
01380             return -code error "nodes must be in the same document"
01381             }
01382             set idx [lsearch [set $node(node:childNodes)] [lindex $args 1]]
01383             if {$idx < 0} {
01384             return -code error "no such reference child"
01385             } else {
01386 
01387             # Remove from previous parent
01388             if {[string length $newChild(node:parentNode)]} {
01389                 node removeChild $newChild(node:parentNode) [lindex $args 0]
01390             }
01391 
01392             # Insert into new node
01393             set $node(node:childNodes) \
01394                 [linsert [set $node(node:childNodes)] $idx [lindex $args 0]]
01395             set newChild(node:parentNode) $token
01396             }
01397         }
01398         }
01399 
01400         event postMutationEvent [lindex $args 0] DOMNodeInserted -relatedNode $token
01401         FireNodeInsertedEvents [lindex $args 0]
01402         event postMutationEvent $token DOMSubtreeModified
01403 
01404         set result [lindex $args 0]
01405 
01406     }
01407 
01408     rep* {
01409 
01410         # replaceChild
01411 
01412         if {[llength $args] != 2} {
01413         return -code error "wrong # args: should be \"dom::node replaceChild token new old\""
01414         }
01415 
01416         upvar #0 [lindex $args 0] newChild
01417         upvar #0 [lindex $args 1] oldChild
01418         upvar #0 $node(node:childNodes) children
01419 
01420         # Find where to insert new child
01421         set idx [lsearch $children [lindex $args 1]]
01422         if {$idx < 0} {
01423         return -code error "no such old child"
01424         }
01425 
01426         # Remove new child from current parent
01427         if {[string length $newChild(node:parentNode)]} {
01428         node removeChild $newChild(node:parentNode) [lindex $args 0]
01429         }
01430 
01431         set children \
01432         [lreplace $children $idx $idx [lindex $args 0]]
01433         set newChild(node:parentNode) $token
01434 
01435         # Update old child to reflect lack of parentage
01436         set oldChild(node:parentNode) {}
01437 
01438         set result [lindex $args 1]
01439 
01440         event postMutationEvent [lindex $args 0] DOMNodeInserted -relatedNode $token
01441         FireNodeInsertedEvents [lindex $args 0]
01442         event postMutationEvent $token DOMSubtreeModified
01443 
01444     }
01445 
01446     removeC* {
01447 
01448         # removeChild
01449 
01450         if {[llength $args] != 1} {
01451         return -code error "wrong # args: should be \"dom::node removeChild token child\""
01452         }
01453         upvar #0 [lindex $args 0] oldChild
01454         if {[string compare [namespace qualifiers [lindex $args 0]] [namespace qualifiers $token]]} {
01455         return -code error "node \"[lindex $args 0]\" is not a child"
01456         }
01457 
01458         # Remove the child from the parent
01459         upvar #0 $node(node:childNodes) myChildren
01460         if {[set idx [lsearch $myChildren [lindex $args 0]]] < 0} {
01461         return -code error "node \"[lindex $args 0]\" is not a child"
01462         }
01463         set myChildren [lreplace $myChildren $idx $idx]
01464 
01465         # Update the child to reflect lack of parentage
01466         set oldChild(node:parentNode) {}
01467 
01468         set result [lindex $args 0]
01469 
01470         # Event propagation has a problem here:
01471         # Nodes that until recently were ancestors may
01472         # want to capture the event, but we've just removed
01473         # the parentage information.  They get a DOMSubtreeModified
01474         # instead.
01475         event postMutationEvent [lindex $args 0] DOMNodeRemoved -relatedNode $token
01476         FireNodeRemovedEvents [lindex $args 0]
01477         event postMutationEvent $token DOMSubtreeModified
01478 
01479     }
01480 
01481     ap* {
01482 
01483         # appendChild
01484 
01485         if {[llength $args] != 1} {
01486         return -code error "wrong # args: should be \"dom::node appendChild token child\""
01487         }
01488 
01489         # Add to new parent
01490         node insertBefore $token [lindex $args 0]
01491 
01492         set result [lindex $args 0]
01493 
01494     }
01495 
01496     hasChildNodes {
01497         set result [Min 1 [llength [set $node(node:childNodes)]]]
01498     }
01499 
01500     isSameNode {
01501         # Introduced in DOM Level 3
01502         switch [llength $args] {
01503         1 {
01504             return [expr {$token == [lindex $args 0]}]
01505         }
01506         default {
01507             return -code error "wrong # args: should be \"dom::node isSameNode token ref\""
01508         }
01509         }
01510     }
01511 
01512     cl* {
01513         # cloneNode
01514 
01515         # May need to pay closer attention to generation of events here
01516 
01517         set deep 0
01518         switch [llength $args] {
01519         0 {
01520         }
01521         2 {
01522             foreach {opt value} $args {
01523             switch -- $opt {
01524                 -deep {
01525                 set deep [Boolean $value]
01526                 }
01527                 default {
01528                 return -code error "bad option \"$opt\""
01529                 }
01530             }
01531             }
01532         }
01533         default {
01534             return -code error "wrong # args: should be \"dom::node cloneNode token ?-deep boolean?\""
01535         }
01536         }
01537 
01538         switch $node(node:nodeType) {
01539         element {
01540             set result [CreateElement {} $node(node:nodeName) [array get $node(element:attributeList)] -document [namespace qualifiers $token]::Document]
01541             if {$deep} {
01542             foreach child [set $node(node:childNodes)] {
01543                 node appendChild $result [node cloneNode $child -deep 1]
01544             }
01545             }
01546         }
01547         textNode {
01548             set result [CreateTextNode {} $node(node:nodeValue) -document [namespace qualifiers $token]::Document]
01549         }
01550         document {
01551             set result [DOMImplementation create]
01552             upvar #0 $result clonedDoc
01553             array set clonedDoc [array get node document:doctype]
01554             if {$deep} {
01555             foreach child [set $node(node:childNodes)] {
01556                 node appendChild $result [document importNode $result $child -deep 1]
01557             }
01558             }
01559         }
01560         documentFragment -
01561         default {
01562             set result [CreateGeneric {} node:nodeType $node(node:nodeType) -document [namespace qualifiers $token]::Document]
01563             if {$deep} {
01564             foreach child [set $node(node:childNodes)] {
01565                 node appendChild $result [node cloneNode $child -deep 1]
01566             }
01567             }
01568         }
01569         }
01570     }
01571 
01572     ch* {
01573         # children -- non-standard method
01574 
01575         # If this is a textNode, then catch the error
01576         set result {}
01577         catch {set result [set $node(node:childNodes)]}
01578 
01579     }
01580 
01581     par* {
01582         # parent -- non-standard method
01583 
01584         return $node(node:parentNode)
01585 
01586     }
01587 
01588     pat* {
01589         # path -- non-standard method
01590 
01591         for {
01592         set ancestor $token
01593         upvar #0 $token ancestorNd
01594         set result {}
01595         } {[string length $ancestorNd(node:parentNode)]} {
01596         set ancestor $ancestorNd(node:parentNode)
01597         upvar #0 $ancestor ancestorNd
01598         } {
01599         set result [linsert $result 0 $ancestor]
01600         }
01601         # The last node is the document node
01602         set result [linsert $result 0 $ancestor]
01603 
01604     }
01605 
01606     createNode {
01607         # createNode -- non-standard method
01608 
01609         # Creates node(s) in this document given an XPath expression.
01610         # Relative location paths have this node as their initial context.
01611 
01612         if {[llength $args] != 1} {
01613         return -code error "wrong # args: should be \"dom::node createNode token path\""
01614         }
01615 
01616         package require xpath
01617 
01618         return [XPath:CreateNode $token [lindex $args 0]]
01619     }
01620 
01621     selectNode {
01622         # selectNode -- non-standard method
01623 
01624         # Returns nodeset in this document matching an XPath expression.
01625         # Relative location paths have this node as their initial context.
01626 
01627         if {[llength $args] != 1} {
01628         return -code error "wrong # args: should be \"dom::node selectNode token path\""
01629         }
01630 
01631         package require xpath
01632 
01633         return [XPath:SelectNode $token [lindex $args 0]]
01634     }
01635 
01636     stringValue {
01637         # stringValue -- non-standard method
01638         # Returns string value of a node, as defined by XPath Rec.
01639 
01640         if {[llength $args] > 0} {
01641         return -code error "wrong # args: should be \"dom::node stringValue token\""
01642         }
01643 
01644         switch $node(node:nodeType) {
01645         document -
01646         documentFragment -
01647         element {
01648             set value {}
01649             foreach child [set $node(node:childNodes)] {
01650             switch [node cget $child -nodeType] {
01651                 element -
01652                 textNode {
01653                 append value [node stringValue $child]
01654                 }
01655                 default {
01656                 # Other nodes are not considered
01657                 }
01658             }
01659             }
01660             return $value
01661         }
01662         attribute -
01663         textNode -
01664         processingInstruction -
01665         comment {
01666             return $node(node:nodeValue)
01667         }
01668         default {
01669             return {}
01670         }
01671         }
01672 
01673     }
01674 
01675     addEv* {
01676         # addEventListener -- introduced in DOM Level 2
01677 
01678         if {[llength $args] < 1} {
01679         return -code error "wrong # args: should be \"dom::node addEventListener token type ?listener? ?option value...?\""
01680         }
01681 
01682         set type [lindex $args 0]
01683         set args [lrange $args 1 end]
01684         set listener [lindex $args 0]
01685         if {[llength $args] == 1} {
01686         set args {}
01687         } elseif {[llength $args] > 1} {
01688         if {[string match -* $listener]} {
01689             set listener {}
01690         } else {
01691             set args [lrange $args 1 end]
01692         }
01693         }
01694         array set opts {-usecapture 0}
01695         if {[catch {array set opts $args}]} {
01696         return -code error "missing value for option \"[lindex $args end]\""
01697         }
01698         set opts(-usecapture) [Boolean $opts(-usecapture)]
01699         set listenerType [expr {$opts(-usecapture) ? "capturer" : "listener"}]
01700 
01701         if {[string length $listener]} {
01702         if {![info exists node(event:$type:$listenerType)] || \
01703             [lsearch $node(event:$type:$listenerType) $listener] < 0} {
01704             lappend node(event:$type:$listenerType) $listener
01705         }
01706         # else avoid registering same listener twice
01707         } else {
01708         # List all listeners
01709         set result {}
01710         catch {set result $node(event:$type:$listenerType)}
01711         return $result
01712         }
01713     }
01714 
01715     removeE* {
01716         # removeEventListener -- introduced in DOM Level 2
01717 
01718         if {[llength $args] < 2} {
01719         return -code error "wrong # args: should be \"dom::node removeEventListener token type listener ?option value...?\""
01720         }
01721 
01722         set type [lindex $args 0]
01723         set listener [lindex $args 1]
01724         array set opts {-usecapture 0}
01725         array set opts [lrange $args 2 end]
01726         set opts(-usecapture) [Boolean $opts(-usecapture)]
01727         set listenerType [expr {$opts(-usecapture) ? "capturer" : "listener"}]
01728 
01729         set idx [lsearch $node(event:$type:$listenerType) $listener]
01730         if {$idx >= 0} {
01731         set node(event:$type:$listenerType) [lreplace $node(event:$type:$listenerType) $idx $idx]
01732         }
01733 
01734     }
01735 
01736     disp* {
01737         # dispatchEvent -- introduced in DOM Level 2
01738 
01739         # This is where the fun happens!
01740         # Check to see if there one or more event listener,
01741         # if so trigger the listener(s).
01742         # Then pass the event up to the ancestor.
01743         # This may be modified by event capturing and bubbling.
01744 
01745         if {[llength $args] != 1} {
01746         return -code error "wrong # args: should be \"dom::node dispatchEvent token eventnode\""
01747         }
01748 
01749         set eventId [lindex $args 0]
01750         upvar #0 $eventId event
01751         set type $event(type)
01752 
01753         if {![string length $event(eventPhase)]} {
01754 
01755         # This is the initial dispatch of the event.
01756         # First trigger any capturing event listeners
01757         # Starting from the root, proceed downward
01758 
01759         set event(eventPhase) capturing_phase
01760         set event(target) $token
01761 
01762         # DOM L2 specifies that the ancestors are determined
01763         # at the moment of event dispatch, so using a static
01764         # list is the correct thing to do
01765 
01766         foreach ancestor [lreplace [node path $token] end end] {
01767             set event(currentNode) $ancestor
01768 
01769             upvar #0 $ancestor ancNode
01770 
01771             if {[info exists ancNode(event:$type:capturer)]} {
01772             foreach capturer $ancNode(event:$type:capturer) {
01773                 if {[catch {uplevel #0 $capturer [list $eventId]} capturerError]} {
01774                 bgerror "error in capturer \"$capturerError\""
01775                 }
01776             }
01777 
01778             # A listener may stop propagation,
01779             # but we check here to let all of the
01780             # listeners at that level complete
01781 
01782             if {$event(cancelable) && $event(stopPropagation)} {
01783                 break
01784             }
01785             }
01786         }
01787 
01788         # Prepare for next phase
01789         set event(eventPhase) at_target
01790 
01791         }
01792 
01793         set event(currentNode) $token
01794 
01795         if {[info exists node(event:$type:listener)]} {
01796         foreach listener $node(event:$type:listener) {
01797             if {[catch {uplevel #0 $listener [list $eventId]} listenerError]} {
01798             bgerror "error in listener \"$listenerError\""
01799             }
01800         }
01801         }
01802 
01803         set event(eventPhase) bubbling_phase
01804 
01805         # Now propagate the event
01806         if {$event(cancelable) && $event(stopPropagation)} {
01807         # Event has been cancelled
01808         } elseif {[llength $node(node:parentNode)]} {
01809         # Go ahead and propagate
01810         node dispatchEvent $node(node:parentNode) $eventId
01811         }
01812 
01813         set event(dispatched) 1
01814     }
01815 
01816     default {
01817         return -code error "unknown method \"$method\""
01818     }
01819 
01820     }
01821 
01822     return $result
01823 }
01824 
01825 /*  dom::tcl::Node:create --*/
01826 /* */
01827 /*  Generic node creation.*/
01828 /*  See also CreateElement, CreateTextNode, CreateGeneric.*/
01829 /* */
01830 /*  Arguments:*/
01831 /*  pVar    array in caller which contains parent details*/
01832 /*  args    configuration options*/
01833 /* */
01834 /*  Results:*/
01835 /*  New child node created.*/
01836 
01837 ret  dom::tcl::Node:create (type pVar , type args) {
01838     upvar #0 $pVar parent
01839 
01840     array set opts {-name {} -value {}}
01841     array set opts $args
01842 
01843     upvar #0 [namespace qualifiers $pVar]::Document document
01844 
01845     # Create new node
01846     if {![info exists opts(-id)]} {
01847     set opts(-id) node[incr document(counter)]
01848     }
01849     set child [namespace qualifiers $pVar]::$opts(-id)
01850     upvar #0 $child new
01851     array set new [list \
01852         node:parentNode $opts(-parent)  \
01853         node:childNodes ${child}var     \
01854         node:nodeType $opts(-type)      \
01855         node:nodeName $opts(-name)      \
01856         node:nodeValue $opts(-value)    \
01857         element:attributeList ${child}arr   \
01858     ]
01859     set ${child}var {}
01860     array set ${child}arr {}
01861 
01862     # Update parent node
01863     if {![info exists parent(document:documentElement)]} {
01864     lappend parent(node:childNodes) $child
01865     }
01866 
01867     proc $child {method args} "return \[eval [namespace current]::node \[list \$method\] $child \$args\]"
01868     trace add command $child delete [namespace code [list Node:Delete $child]]
01869 
01870     return $child
01871 }
01872 
01873 /*  dom::tcl::Node:set --*/
01874 /* */
01875 /*  Generic node update*/
01876 /* */
01877 /*  Arguments:*/
01878 /*  token   node token*/
01879 /*  args    configuration options*/
01880 /* */
01881 /*  Results:*/
01882 /*  Node modified.*/
01883 
01884 ret  dom::tcl::Node:set (type token , type args) {
01885     upvar #0 $token node
01886 
01887     foreach {key value} $args {
01888     set node($key) $value
01889     }
01890 
01891     return {}
01892 }
01893 
01894 /*  dom::tcl::Node:Delete --*/
01895 /* */
01896 /*  Handle node destruction*/
01897 /* */
01898 /*  Arguments:*/
01899 /*  name    node token*/
01900 /*  old )*/
01901 /*  new ) arguments appended by trace command*/
01902 /*  op  )*/
01903 /* */
01904 /*  Results:*/
01905 /*  Node is destroyed*/
01906 
01907 ret  dom::tcl::Node:Delete (type name , type old , type new , type op) {
01908     if {[catch {DOMImplementation destroy $name} ret]} {
01909     # Document has been deleted... namespace has been destroyed
01910     } else {
01911     return $ret
01912     }
01913 }
01914 
01915 /*  dom::tcl::FireNodeInsertedEvents --*/
01916 /* */
01917 /*  Recursively descend the tree triggering DOMNodeInserted*/
01918 /*  events as we go.*/
01919 /* */
01920 /*  Arguments:*/
01921 /*  nodeid  Node ID*/
01922 /* */
01923 /*  Results:*/
01924 /*  DOM L2 DOMNodeInserted events posted*/
01925 
01926 ret  dom::tcl::FireNodeInsertedEvents nodeid (
01927     type event , type postMutationEvent $, type nodeid , type DOMNodeInsertedIntoDocument
01928     , type foreach , type child [, type node , type children $, type nodeid] , optional 
01929     FireNodeInsertedEvents =$child
01930     
01931 
01932     , type return , optional 
01933 )
01934 
01935 # dom::tcl::FireNodeRemovedEvents --
01936 #
01937 #   Recursively descend the tree triggering DOMNodeRemoved
01938 #   events as we go.
01939 #
01940 # Arguments:
01941 #   nodeid  Node ID
01942 #
01943 # Results:
01944 #   DOM L2 DOMNodeRemoved events posted
01945 
01946 proc dom::tcl::FireNodeRemovedEvents nodeid {
01947     event postMutationEvent $nodeid DOMNodeRemovedFromDocument
01948     foreach child [node children $nodeid] {
01949     FireNodeRemovedEvents $child
01950     }
01951 
01952     return {}
01953 }
01954 
01955 /*  dom::tcl::element --*/
01956 /* */
01957 /*  Functions for an element.*/
01958 /* */
01959 /*  Arguments:*/
01960 /*  method  method to invoke*/
01961 /*  token   token for node*/
01962 /*  args    arguments for method*/
01963 /* */
01964 /*  Results:*/
01965 /*  Depends on method used.*/
01966 
01967 namespace dom::tcl {
01968     variable elementOptionsRO tagName|empty
01969     variable elementOptionsRW {}
01970 }
01971 
01972 ret  dom::tcl::element (type method , type token , type args) {
01973     variable elementOptionsRO
01974     variable elementOptionsRW
01975 
01976     upvar #0 $token node
01977 
01978     if {[string compare $node(node:nodeType) "element"]} {
01979     return -code error "malformed node token \"$token\""
01980     }
01981     set result {}
01982 
01983     switch -- $method {
01984 
01985     cget {
01986         # Some read-only configuration options are computed
01987         if {[llength $args] != 1} {
01988         return -code error "wrong # args: should be \"dom::element cget token option\""
01989         }
01990         if {[regexp [format {^-(%s)$} $elementOptionsRO] [lindex $args 0] discard option]} {
01991         switch $option {
01992             tagName {
01993             set result [lindex $node(node:nodeName) 0]
01994             }
01995             empty {
01996             if {![info exists node(element:empty)]} {
01997                 return 0
01998             } else {
01999                 return $node(element:empty)
02000             }
02001             }
02002             default {
02003             return $node(node:$option)
02004             }
02005         }
02006         } elseif {[regexp [format {^-(%s)$} $elementOptionsRW] [lindex $args 0] discard option]} {
02007         return $node(node:$option)
02008         } else {
02009         return -code error "bad option \"[lindex $args 0]\""
02010         }
02011     }
02012     configure {
02013         if {[llength $args] == 1} {
02014         return [document cget $token [lindex $args 0]]
02015         } elseif {[expr [llength $args] % 2]} {
02016         return -code error "no value specified for option \"[lindex $args end]\""
02017         } else {
02018         foreach {option value} $args {
02019             if {[regexp [format {^-(%s)$} $elementOptionsRO] $option discard opt]} {
02020             return -code error "option \"$option\" cannot be modified"
02021             } elseif {[regexp [format {^-(%s)$} $elementOptionsRW] $option discard opt]} {
02022             return -code error "not implemented"
02023             } else {
02024             return -code error "bad option \"$option\""
02025             }
02026         }
02027         }
02028     }
02029 
02030     getAttribute {
02031         if {[llength $args] != 1} {
02032         return -code error "wrong # args: should be \"dom::element getAttribute token name\""
02033         }
02034 
02035         set result {}
02036 
02037         upvar #0 $node(element:attributeList) attrList
02038         catch {set result $attrList([lindex $args 0])}
02039 
02040         return $result
02041 
02042     }
02043 
02044     setAttribute {
02045         if {[llength $args] != 2} {
02046         return -code error "wrong # args: should be \"dom::element setAttribute token name value\""
02047         }
02048 
02049         # Check that the attribute name is kosher
02050         if {![regexp ^$::xml::Name\$ [lindex $args 0]]} {
02051         return -code error "invalid attribute name \"[lindex $args 0]\""
02052         }
02053 
02054         upvar #0 $node(element:attributeList) attrList
02055         set evid [CreateEvent $token DOMAttrModified]
02056         set oldValue {}
02057         catch {set oldValue $attrList([lindex $args 0])}
02058         event initMutationEvent $evid DOMAttrModified 1 0 $token $oldValue [lindex $args 1] [lindex $args 0] [expr {[info exists attrList([lindex $args 0])] ? "modification" : "addition"}]
02059         set result [set attrList([lindex $args 0]) [lindex $args 1]]
02060         node dispatchEvent $token $evid
02061         DOMImplementation destroy $evid
02062 
02063     }
02064 
02065     removeAttribute {
02066         if {[llength $args] != 1} {
02067         return -code error "wrong # args: should be \"dom::element removeAttribute token name\""
02068         }
02069 
02070         upvar #0 $node(element:attributeList) attrList
02071         catch {unset attrList([lindex $args 0])}
02072 
02073         event postMutationEvent $token DOMAttrRemoved -attrName [lindex $args 0] -attrChange removal
02074 
02075     }
02076 
02077     getAttributeNS {
02078         if {[llength $args] != 2} {
02079         return -code error "wrong # args: should be \"dom::element getAttributeNS token ns name\""
02080         }
02081 
02082         set result {}
02083         upvar #0 $node(element:attributeList) attrList
02084         catch {set result $attrList([lindex $args 0]^[lindex $args 1])}
02085 
02086         return $result
02087 
02088     }
02089 
02090     setAttributeNS {
02091         if {[llength $args] != 3} {
02092         return -code error "wrong # args: should be \"dom::element setAttributeNS token ns attr value\""
02093         }
02094 
02095         # Check that the attribute name is kosher
02096         if {![regexp ^$::xml::QName\$ [lindex $args 1] discard prefix localName]} {
02097         return -code error "invalid qualified attribute name \"[lindex $args 1]\""
02098         }
02099 
02100         # BUG: At the moment the prefix is ignored
02101 
02102         upvar #0 $node(element:attributeList) attrList
02103         set evid [CreateEvent $token DOMAttrModified]
02104         set oldValue {}
02105         catch {set oldValue $attrList([lindex $args 0]^$localName)}
02106         event initMutationEvent $evid DOMAttrModified 1 0 $token $oldValue [lindex $args 2] [lindex $args 0]^localName [expr {[info exists attrList([lindex $args 0]^$localName)] ? "modification" : "addition"}]
02107         set result [set attrList([lindex $args 0]^$localName) [lindex $args 2]]
02108         node dispatchEvent $token $evid
02109         DOMImplementation destroy $evid
02110 
02111     }
02112 
02113     removeAttributeNS {
02114         if {[llength $args] != 2} {
02115         return -code error "wrong # args: should be \"dom::element removeAttributeNS token ns name\""
02116         }
02117 
02118         upvar #0 $node(element:attributeList) attrList
02119         catch {unset attrList([lindex $args 0]^[lindex $args 1])}
02120 
02121         event postMutationEvent $token DOMAttrRemoved -attrName [lindex $args 0]^[lindex $args 1] -attrChange removal
02122 
02123     }
02124 
02125     getAttributeNode {
02126         array set tmp [array get $node(element:attributeList)]
02127         if {![info exists tmp([lindex $args 0])]} {
02128         return {}
02129         }
02130 
02131         # Synthesize an attribute node if one doesn't already exist
02132         array set attrNodes $node(element:attributeNodes)
02133         if {[catch {set result $attrNodes([lindex $args 0])}]} {
02134         set result [CreateGeneric $token node:nodeType attribute node:nodeName [lindex $args 0] node:nodeValue $tmp([lindex $args 0])]
02135         lappend node(element:attributeNodes) [lindex $args 0] $result
02136         }
02137     }
02138 
02139     setAttributeNode -
02140     removeAttributeNode -
02141     getAttributeNodeNS -
02142     setAttributeNodeNS -
02143     removeAttributeNodeNS {
02144         return -code error "not yet implemented"
02145     }
02146 
02147     getElementsByTagName {
02148         if {[llength $args] < 1} {
02149         return -code error "wrong # args: should be \"dom::element getElementsByTagName token name\""
02150         }
02151 
02152         return [eval Element:GetByTagName [list $token [lindex $args 0]] \
02153             [lrange $args 1 end]]
02154     }
02155 
02156     normalize {
02157         if {[llength $args]} {
02158         return -code error "wrong # args: should be dom::element normalize token"
02159         }
02160 
02161         Element:Normalize node [set $node(node:childNodes)]
02162     }
02163 
02164     default {
02165         return -code error "bad method \"$method\": should be cget, configure, getAttribute, setAttribute, removeAttribute, getAttributeNS, setAttributeNS, removeAttributeNS, getAttributeNode, setAttributeNode, removeAttributeNode, getAttributeNodeNS, setAttributeNodeNS, removeAttributeNodeNS, getElementsByTagName or normalize"
02166     }
02167 
02168     }
02169 
02170     return $result
02171 }
02172 
02173 /*  dom::tcl::Element:GetByTagName --*/
02174 /* */
02175 /*  Search for (child) elements*/
02176 /* */
02177 /*  This used to be non-recursive, but then I read the DOM spec*/
02178 /*  properly and discovered that it should recurse.  The -deep*/
02179 /*  option allows for backward-compatibility, and defaults to the*/
02180 /*  DOM-specified value of true.*/
02181 /* */
02182 /*  Arguments:*/
02183 /*  token   parent node*/
02184 /*  name    element type to search for*/
02185 /*  args    configuration options*/
02186 /* */
02187 /*  Results:*/
02188 /*  The name of the variable containing the list of matching node tokens*/
02189 
02190 ret  dom::tcl::Element:GetByTagName (type token , type name , type args) {
02191     upvar #0 $token node
02192     upvar #0 [namespace qualifiers $token]::Document document
02193 
02194     array set cfg {-deep 1}
02195     array set cfg $args
02196     set cfg(-deep) [Boolean $cfg(-deep)]
02197 
02198     # Guard against arbitrary glob characters
02199     # Checking that name is a legal XML Name does this
02200     # However, '*' is permitted
02201     if {![regexp ^$::xml::Name\$ $name] && [string compare $name "*"]} {
02202     return -code error "invalid element name"
02203     }
02204 
02205     # Allocate variable name for this search
02206     set searchVar ${token}search[incr document(counter)]
02207     upvar \#0 $searchVar search
02208 
02209     # Make list live by interposing on variable reads
02210     # I don't think we need to interpose on unsets,
02211     # and writing to this variable by the application is
02212     # not permitted.
02213 
02214     trace variable $searchVar w [namespace code Element:GetByTagName:Error]
02215 
02216     if {[string compare $node(node:nodeType) "document"]} {
02217     trace variable $searchVar r [namespace code [list Element:GetByTagName:Search [set $node(node:childNodes)] $name $cfg(-deep)]]
02218     } elseif {[llength $node(document:documentElement)]} {
02219     # Document Element must exist and must be an element type node
02220     trace variable $searchVar r [namespace code [list Element:GetByTagName:Search $node(document:documentElement) $name $cfg(-deep)]]
02221     }
02222 
02223     return $searchVar
02224 }
02225 
02226 /*  dom::tcl::Element:GetByTagName:Search --*/
02227 /* */
02228 /*  Search for elements.  This does the real work.*/
02229 /*  Because this procedure is invoked everytime*/
02230 /*  the variable is read, it returns the live list.*/
02231 /* */
02232 /*  Arguments:*/
02233 /*  tokens  nodes to search (inclusive)*/
02234 /*  name    element type to search for*/
02235 /*  deep    whether to search recursively*/
02236 /*  name1   \*/
02237 /*  name2    > appended by trace command*/
02238 /*  op  /*/
02239 /* */
02240 /*  Results:*/
02241 /*  List of matching node tokens*/
02242 
02243 ret  dom::tcl::Element:GetByTagName:Search (type tokens , type name , type deep , type name1 , type name2 , type op) {
02244     set result {}
02245 
02246     foreach tok $tokens {
02247     upvar #0 $tok nodeInfo
02248     switch -- $nodeInfo(node:nodeType) {
02249         element {
02250         if {[string match $name [GetField nodeInfo(node:nodeName)]]} {
02251             lappend result $tok
02252         }
02253         if {$deep} {
02254             set childResult [Element:GetByTagName:Search [set $nodeInfo(node:childNodes)] $name $deep {} {} {}]
02255             if {[llength $childResult]} {
02256             eval lappend result $childResult
02257             }
02258         }
02259         }
02260     }
02261     }
02262 
02263     if {[string length $name1]} {
02264     set $name1 $result
02265     return {}
02266     } else {
02267     return $result
02268     }
02269 }
02270 
02271 /*  dom::tcl::Element:GetByTagName:Error --*/
02272 /* */
02273 /*  Complain about the application writing to a variable*/
02274 /*  that this package maintains.*/
02275 /* */
02276 /*  Arguments:*/
02277 /*  name1   \*/
02278 /*  name2    > appended by trace command*/
02279 /*  op  /*/
02280 /* */
02281 /*  Results:*/
02282 /*  Error code returned.*/
02283 
02284 ret  dom::tcl::Element:GetByTagName:Error (type name1 , type name2 , type op) {
02285     return -code error "dom: Read-only variable"
02286 }
02287 
02288 /*  dom::tcl::Element:Normalize --*/
02289 /* */
02290 /*  Normalize the text nodes*/
02291 /* */
02292 /*  Arguments:*/
02293 /*  pVar    parent array variable in caller*/
02294 /*  nodes   list of node tokens*/
02295 /* */
02296 /*  Results:*/
02297 /*  Adjacent text nodes are coalesced*/
02298 
02299 ret  dom::tcl::Element:Normalize (type pVar , type nodes) {
02300     upvar #0 $pVar parent
02301 
02302     set textNode {}
02303 
02304     foreach n $nodes {
02305     upvar #0 $n child
02306     set cleanup {}
02307 
02308     switch $child(node:nodeType) {
02309         textNode {
02310         if {[llength $textNode]} {
02311 
02312             # Coalesce into previous node
02313             set evid [CreateEvent $n DOMCharacterDataModified]
02314             event initMutationEvent $evid DOMCharacterDataModified 1 0 {} $text(node:nodeValue) $text(node:nodeValue)$child(node:nodeValue) {} {}
02315             append text(node:nodeValue) $child(node:nodeValue)
02316             node dispatchEvent $n $evid
02317             DOMImplementation destroy $evid
02318 
02319             # Remove this child
02320             upvar #0 $parent(node:childNodes) childNodes
02321             set idx [lsearch $childNodes $n]
02322             set childNodes [lreplace $childNodes $idx $idx]
02323             unset $n
02324             set cleanup [list event postMutationEvent [node parent $n] DOMSubtreeModified]
02325             event postMutationEvent $n DOMNodeRemoved
02326 
02327             set $textNode [array get text]
02328         } else {
02329             set textNode $n
02330             catch {unset text}
02331             array set text [array get child]
02332         }
02333         }
02334         element -
02335         document -
02336         documentFragment {
02337         set textNode {}
02338         Element:Normalize child [set $child(node:childNodes)]
02339         }
02340         default {
02341         set textNode {}
02342         }
02343     }
02344 
02345     eval $cleanup
02346     }
02347 
02348     return {}
02349 }
02350 
02351 /*  dom::tcl::processinginstruction --*/
02352 /* */
02353 /*  Functions for a processing intruction.*/
02354 /* */
02355 /*  Arguments:*/
02356 /*  method  method to invoke*/
02357 /*  token   token for node*/
02358 /*  args    arguments for method*/
02359 /* */
02360 /*  Results:*/
02361 /*  Depends on method used.*/
02362 
02363 namespace dom::tcl {
02364     variable piOptionsRO target
02365     variable piOptionsRW data
02366 }
02367 
02368 ret  dom::tcl::processinginstruction (type method , type token , type args) {
02369     variable piOptionsRO
02370     variable piOptionsRW
02371 
02372     upvar #0 $token node
02373 
02374     set result {}
02375 
02376     switch -- $method {
02377 
02378     cget {
02379         # Some read-only configuration options are computed
02380         if {[llength $args] != 1} {
02381         return -code error "too many arguments"
02382         }
02383         if {[regexp [format {^-(%s)$} $elementOptionsRO] [lindex $args 0] discard option]} {
02384         switch $option {
02385             target {
02386             set result [lindex $node(node:nodeName) 0]
02387             }
02388             default {
02389             return $node(node:$option)
02390             }
02391         }
02392         } elseif {[regexp [format {^-(%s)$} $elementOptionsRW] [lindex $args 0] discard option]} {
02393         switch $option {
02394             data {
02395             return $node(node:nodeValue)
02396             }
02397             default {
02398             return $node(node:$option)
02399             }
02400         }
02401         } else {
02402         return -code error "unknown option \"[lindex $args 0]\""
02403         }
02404     }
02405     configure {
02406         if {[llength $args] == 1} {
02407         return [document cget $token [lindex $args 0]]
02408         } elseif {[expr [llength $args] % 2]} {
02409         return -code error "no value specified for option \"[lindex $args end]\""
02410         } else {
02411         foreach {option value} $args {
02412             if {[regexp [format {^-(%s)$} $elementOptionsRO] $option discard opt]} {
02413             return -code error "attribute \"$option\" is read-only"
02414             } elseif {[regexp [format {^-(%s)$} $elementOptionsRW] $option discard opt]} {
02415             switch $opt {
02416                 data {
02417                 set evid [CreateEvent $token DOMCharacterDataModified]
02418                 event initMutationEvent $evid DOMCharacterModified 1 0 {} $node(node:nodeValue) $value {} {}
02419                 set node(node:nodeValue) $value
02420                 node dispatchEvent $token $evid
02421                 DOMImplementation destroy $evid
02422                 }
02423                 default {
02424                 set node(node:$opt) $value
02425                 }
02426             }
02427             } else {
02428             return -code error "unknown option \"$option\""
02429             }
02430         }
02431         }
02432     }
02433 
02434     default {
02435         return -code error "unknown method \"$method\""
02436     }
02437 
02438     }
02439 
02440     return $result
02441 }
02442 
02443 /* */
02444 /* */
02445 /*  DOM Level 2 Interfaces*/
02446 /* */
02447 /* */
02448 
02449 /*  dom::tcl::event --*/
02450 /* */
02451 /*  Implements Event Interface*/
02452 /* */
02453 /*  Subclassed Interfaces are also defined here,*/
02454 /*  such as UIEvents.*/
02455 /* */
02456 /*  Arguments:*/
02457 /*  method  method to invoke*/
02458 /*  token   token for event*/
02459 /*  args    arguments for method*/
02460 /* */
02461 /*  Results:*/
02462 /*  Depends on method used.*/
02463 
02464 namespace dom::tcl {
02465     variable eventOptionsRO type|target|currentNode|eventPhase|bubbles|cancelable|timeStamp|detail|view|screenX|screenY|clientX|clientY|ctrlKey|shiftKey|altKey|metaKey|button|relatedNode|prevValue|newValue|attrName|attrChange
02466     variable eventOptionsRW {}
02467 
02468     /*  Issue: should the attributes belonging to the subclassed Interface*/
02469     /*  be separated out?*/
02470 
02471     variable uieventOptionsRO detail|view
02472     variable uieventOptionsRW {}
02473 
02474     variable mouseeventOptionsRO screenX|screenY|clientX|clientY|ctrlKey|shiftKey|altKey|metaKey|button|relatedNode
02475     variable mouseeventOptionsRW {}
02476 
02477     variable mutationeventOptionsRO relatedNode|prevValue|newValue|attrName
02478     variable mutationeventOptionsRW {}
02479 }
02480 
02481 ret  dom::tcl::event (type method , type token , type args) {
02482     variable eventOptionsRO
02483     variable eventOptionsRW
02484 
02485     upvar #0 $token event
02486 
02487     set result {}
02488 
02489     switch -glob -- $method {
02490 
02491     cg* {
02492         # cget
02493 
02494         if {[llength $args] != 1} {
02495         return -code error "too many arguments"
02496         }
02497         if {[regexp [format {^-(%s)$} $eventOptionsRO] [lindex $args 0] discard option]} {
02498         return $event($option)
02499         } elseif {[regexp [format {^-(%s)$} $eventOptionsRW] [lindex $args 0] discard option]} {
02500         return $event($option)
02501         } else {
02502         return -code error "unknown option \"[lindex $args 0]\""
02503         }
02504     }
02505 
02506     co* {
02507         # configure
02508 
02509         if {[llength $args] == 1} {
02510         return [event cget $token [lindex $args 0]]
02511         } elseif {[expr [llength $args] % 2]} {
02512         return -code error "no value specified for option \"[lindex $args end]\""
02513         } else {
02514         foreach {option value} $args {
02515             if {[regexp [format {^-(%s)$} $eventOptionsRW] $option discard opt]} {
02516             set event($opt) $value
02517             } elseif {[regexp [format {^-(%s)$} $eventOptionsRO] $option discard opt]} {
02518             return -code error "attribute \"$option\" is read-only"
02519             } else {
02520             return -code error "unknown option \"$option\""
02521             }
02522         }
02523         }
02524 
02525     }
02526 
02527     st* {
02528         # stopPropagation
02529 
02530         set event(stopPropagation) 1
02531     }
02532 
02533     pr* {
02534         # preventDefault
02535 
02536         set event(preventDefault) 1
02537     }
02538 
02539     initE* {
02540         # initEvent
02541 
02542         if {[llength $args] != 3} {
02543         return -code error "wrong # args: should be dom::event initEvent token type bubbles cancelable"
02544         }
02545 
02546         if {$event(dispatched)} {
02547         return -code error "event has been dispatched"
02548         }
02549 
02550         foreach {event(type) event(bubbles) event(cancelable)} $args break
02551     }
02552 
02553     initU* {
02554         # initUIEvent
02555 
02556         if {[llength $args] < 4 || [llength $args] > 5} {
02557         return -code error "wrong # args: should be dom::event initUIEvent token type bubbles cancelable view detail"
02558         }
02559 
02560         if {$event(dispatched)} {
02561         return -code error "event has been dispatched"
02562         }
02563 
02564         set event(detail) 0
02565         foreach {event(type) event(bubbles) event(cancelable) event(view) event(detail)} $args break
02566     }
02567 
02568     initMo* {
02569         # initMouseEvent
02570 
02571         if {[llength $args] != 15} {
02572         return -code error "wrong # args: should be dom::event initMouseEvent token type bubbles cancelable view detail screenX screenY clientX clientY ctrlKey altKey shiftKey metaKey button relatedNode"
02573         }
02574 
02575         if {$event(dispatched)} {
02576         return -code error "event has been dispatched"
02577         }
02578 
02579         set event(detail) 1
02580         foreach {event(type) event(bubbles) event(cancelable) event(view) event(detail) event(screenX) event(screenY) event(clientX) event(clientY) event(ctrlKey) event(altKey) event(shiftKey) event(metaKey) event(button) event(relatedNode)} $args break
02581     }
02582 
02583     initMu* {
02584         # initMutationEvent
02585 
02586         if {[llength $args] != 8} {
02587         return -code error "wrong # args: should be dom::event initMutationEvent token type bubbles cancelable relatedNode prevValue newValue attrName attrChange"
02588         }
02589 
02590         if {$event(dispatched)} {
02591         return -code error "event has been dispatched"
02592         }
02593 
02594         foreach {event(type) event(bubbles) event(cancelable) event(relatedNode) event(prevValue) event(newValue) event(attrName) event(attrChange)} $args break
02595     }
02596 
02597     postUI* {
02598         # postUIEvent, non-standard convenience method
02599 
02600         set evType [lindex $args 0]
02601         array set evOpts [list \
02602             -bubbles $::dom::bubbles($evType) -cancelable $::dom::cancelable($evType)   \
02603             -view {}            \
02604             -detail {}          \
02605         ]
02606         array set evOpts [lrange $args 1 end]
02607 
02608         set evid [CreateEvent $token $evType]
02609         event initUIEvent $evid $evType $evOpts(-bubbles) $evOpts(-cancelable) $evOpts(-view) $evOpts(-detail)
02610         node dispatchEvent $token $evid
02611         DOMImplementation destroy $evid
02612 
02613     }
02614 
02615     postMo* {
02616         # postMouseEvent, non-standard convenience method
02617 
02618         set evType [lindex $args 0]
02619         array set evOpts [list \
02620             -bubbles $::dom::bubbles($evType) -cancelable $::dom::cancelable($evType)   \
02621             -view {}            \
02622             -detail {}          \
02623             -screenX {}         \
02624             -screenY {}         \
02625             -clientX {}         \
02626             -clientY {}         \
02627             -ctrlKey {}         \
02628             -altKey {}          \
02629             -shiftKey {}        \
02630             -metaKey {}         \
02631             -button {}          \
02632             -relatedNode {}     \
02633         ]
02634         array set evOpts [lrange $args 1 end]
02635 
02636         set evid [CreateEvent $token $evType]
02637         event initMouseEvent $evid $evType $evOpts(-bubbles) $evOpts(-cancelable) $evOpts(-view) $evOpts(-detail) $evOpts(-screenX) $evOpts(-screenY) $evOpts(-clientX) $evOpts(-clientY) $evOpts(-ctrlKey) $evOpts(-altKey) $evOpts(-shiftKey) $evOpts(-metaKey) $evOpts(-button) $evOpts(-relatedNode)
02638         node dispatchEvent $token $evid
02639         DOMImplementation destroy $evid
02640 
02641     }
02642 
02643     postMu* {
02644         # postMutationEvent, non-standard convenience method
02645 
02646         set evType [lindex $args 0]
02647         array set evOpts [list \
02648             -bubbles $::dom::bubbles($evType) -cancelable $::dom::cancelable($evType)   \
02649             -relatedNode {}         \
02650             -prevValue {} -newValue {}      \
02651             -attrName {} -attrChange {}     \
02652         ]
02653         array set evOpts [lrange $args 1 end]
02654 
02655         set evid [CreateEvent $token $evType]
02656         event initMutationEvent $evid $evType $evOpts(-bubbles) $evOpts(-cancelable) $evOpts(-relatedNode) $evOpts(-prevValue) $evOpts(-newValue) $evOpts(-attrName) $evOpts(-attrChange)
02657         node dispatchEvent $token $evid
02658         DOMImplementation destroy $evid
02659 
02660     }
02661 
02662     default {
02663         return -code error "unknown method \"$method\""
02664     }
02665     }
02666 
02667     return $result
02668 }
02669 
02670 /*  dom::tcl::CreateEvent --*/
02671 /* */
02672 /*  Create an event object*/
02673 /* */
02674 /*  Arguments:*/
02675 /*  token   parent node*/
02676 /*  type    event type*/
02677 /*  args    configuration options*/
02678 /* */
02679 /*  Results:*/
02680 /*  Returns event token*/
02681 
02682 ret  dom::tcl::CreateEvent (type token , type type , type args) {
02683     array set opts $args
02684     if {[string length $token]} {
02685     upvar #0 $token parent
02686     upvar #0 [namespace qualifiers $token]::Document document
02687     set child [namespace qualifiers $token]::event[incr document(counter)]
02688     } elseif {[info exists $opts(-document)]} {
02689     upvar #0 $opts(-document) document
02690     set child [namespace qualifiers $opts(-document)]::event[incr document(counter)]
02691     }
02692 
02693     upvar #0 $child event
02694 
02695     # Create the event
02696     array set event [list \
02697         node:nodeType event \
02698         type $type      \
02699         target {}       \
02700         currentNode {}  \
02701         cancelable 1    \
02702         stopPropagation 0   \
02703         preventDefault 0    \
02704         dispatched 0    \
02705         bubbles 1       \
02706         eventPhase {}   \
02707         timeStamp [clock clicks -milliseconds]  \
02708         ]
02709 
02710     proc $child {method args} "return \[eval [namespace current]::event \[list \$method\] $child \$args\]"
02711     trace add command $child delete [namespace code [list Node:Delete $child]]
02712 
02713     return $child
02714 }
02715 
02716 /* */
02717 /* */
02718 /*  Serialisation*/
02719 /* */
02720 /* */
02721 
02722 /*  dom::tcl::Serialize:documentFragment --*/
02723 /* */
02724 /*  Produce text for documentFragment.*/
02725 /* */
02726 /*  Arguments:*/
02727 /*  token   node token*/
02728 /*  args    configuration options*/
02729 /* */
02730 /*  Results:*/
02731 /*  XML format text.*/
02732 
02733 ret  dom::tcl::Serialize:documentFragment (type token , type args) {
02734     upvar #0 $token node
02735 
02736     if {[string compare "Document" [namespace tail $token]]} {
02737     return [eval [list Serialize:node $token] $args]
02738     } else {
02739     if {[string compare {} [GetField node(document:documentElement)]]} {
02740         return [eval Serialize:document [list $token] $args]
02741     } else {
02742         return -code error "document has no document element"
02743     }
02744     }
02745 
02746 }
02747 
02748 /*  dom::tcl::Serialize:document --*/
02749 /* */
02750 /*  Produce text for document.*/
02751 /* */
02752 /*  Arguments:*/
02753 /*  token   node token*/
02754 /*  args    configuration options*/
02755 /* */
02756 /*  Results:*/
02757 /*  XML format text.*/
02758 
02759 ret  dom::tcl::Serialize:document (type token , type args) {
02760     upvar #0 $token node
02761     array set opts {
02762     -showxmldecl 1
02763     -showdoctypedecl 1
02764     }
02765     array set opts $args
02766 
02767     set result {}
02768 
02769     if {[string length $node(document:doctype)]} {
02770 
02771     upvar #0 $node(document:doctype) doctype
02772 
02773     # Bug fix: can't use Serialize:attributeList for XML declaration,
02774     # since attributes must occur in a given order (XML 2.8 [23])
02775 
02776     set result {}
02777 
02778     if {$opts(-showxmldecl)} {
02779         append result <?xml[Serialize:XMLDecl version $node(document:xmldecl)][Serialize:XMLDecl encoding $node(document:xmldecl)][Serialize:XMLDecl standalone $node(document:xmldecl)]?>\n
02780     }
02781     if {$opts(-showdoctypedecl)} {
02782         # Is document element in an XML Namespace?
02783         # If so then include prefix in doctype decl
02784         foreach {prefix localName} [::xml::qnamesplit $doctype(doctype:name)] break
02785         if {![string length $prefix]} {
02786         # The prefix may not have been allocated yet
02787         upvar #0 $node(document:documentElement) docel
02788         if {[info exists docel(node:namespaceURI)] && \
02789             [string length $docel(node:namespaceURI)]} {
02790             set declPrefix [GetNamespacePrefix $node(document:documentElement) $docel(node:namespaceURI)]
02791             set docelName $declPrefix:$doctype(doctype:name)
02792         } else {
02793             set docelName $doctype(doctype:name)
02794         }
02795         } else {
02796         set docelName $doctype(doctype:name)
02797         }
02798         # Applied patch by Marco Gonnelli, bug #590914
02799         append result <!DOCTYPE\ $docelName[Serialize:ExternalID $doctype(doctype:publicId) $doctype(doctype:systemId)][expr {[string length $doctype(doctype:internalSubset)] ? " \[[string trim $doctype(doctype:internalSubset) \{\} ]\]" : {}}]>\n
02800     }
02801     }
02802 
02803     # BUG #525505: Want to serialize all children including the
02804     # document element.
02805 
02806     if {[info exists $node(node:childNodes)]} {
02807     foreach child [set $node(node:childNodes)] {
02808         append result [eval Serialize:[node cget $child -nodeType] [list $child] $args]
02809     }
02810     }
02811 
02812     return $result
02813 }
02814 
02815 /*  dom::tcl::Serialize:ExternalID --*/
02816 /* */
02817 /*  Returned appropriately quoted external identifiers*/
02818 /* */
02819 /*  Arguments:*/
02820 /*  publicid    public identifier*/
02821 /*  systemid    system identifier*/
02822 /* */
02823 /*  Results:*/
02824 /*  text*/
02825 
02826 ret  dom::tcl::Serialize:ExternalID (type publicid , type systemid) {
02827 
02828     switch -glob -- [string length $publicid],[string length $systemid] {
02829     0,0 {
02830         return {}
02831     }
02832     0,* {
02833         return " SYSTEM \"$systemid\""
02834     }
02835     *,* {
02836         # Patch from c.l.t., Richard Calmbach (rc@hnc.com )
02837         return " PUBLIC \"$publicid\" \"$systemid\""
02838     }
02839     }
02840 
02841     return {}
02842 }
02843 
02844 /*  dom::tcl::Serialize:XMLDecl --*/
02845 /* */
02846 /*  Produce text for XML Declaration attribute.*/
02847 /*  Order is determine by document serialisation procedure.*/
02848 /* */
02849 /*  Arguments:*/
02850 /*  attr    required attribute*/
02851 /*  attList attribute list*/
02852 /* */
02853 /*  Results:*/
02854 /*  XML format text.*/
02855 
02856 ret  dom::tcl::Serialize:XMLDecl (type attr , type attrList) {
02857     array set data $attrList
02858     if {![info exists data($attr)]} {
02859     return {}
02860     } elseif {[string length $data($attr)]} {
02861     return " $attr='$data($attr)'"
02862     } else {
02863     return {}
02864     }
02865 }
02866 
02867 /*  dom::tcl::Serialize:node --*/
02868 /* */
02869 /*  Produce text for an arbitrary node.*/
02870 /*  This simply serializes the child nodes of the node.*/
02871 /* */
02872 /*  Arguments:*/
02873 /*  token   node token*/
02874 /*  args    configuration options*/
02875 /* */
02876 /*  Results:*/
02877 /*  XML format text.*/
02878 
02879 ret  dom::tcl::Serialize:node (type token , type args) {
02880     upvar #0 $token node
02881     array set opts $args
02882 
02883     if {[info exists opts(-indent)]} {
02884     # NB. 0|1 cannot be used as booleans - mention this in docn
02885     if {[regexp {^false|no|off$} $opts(-indent)]} {
02886         # No action required
02887     } elseif {[regexp {^true|yes|on$} $opts(-indent)]} {
02888         set opts(-indent) 1
02889     } else {
02890         incr opts(-indent)
02891     }
02892     }
02893 
02894     set result {}
02895     foreach childToken [set $node(node:childNodes)] {
02896     upvar #0 $childToken child
02897     append result [eval [list Serialize:$child(node:nodeType) $childToken] [array get opts]]
02898     }
02899 
02900     return $result
02901 }
02902 
02903 /*  dom::tcl::Serialize:element --*/
02904 /* */
02905 /*  Produce text for an element.*/
02906 /* */
02907 /*  Arguments:*/
02908 /*  token   node token*/
02909 /*  args    configuration options*/
02910 /* */
02911 /*  Results:*/
02912 /*  XML format text.*/
02913 
02914 ret  dom::tcl::Serialize:element (type token , type args) {
02915     upvar #0 $token node
02916     array set opts {-newline {}}
02917     array set opts $args
02918 
02919     set result {}
02920     set newline {}
02921     if {[lsearch $opts(-newline) $node(node:nodeName)] >= 0} {
02922     append result \n
02923     set newline \n
02924     }
02925     append result [eval Serialize:Indent [array get opts]]
02926     switch [info exists node(node:namespaceURI)],[info exists node(node:prefix)] {
02927 
02928     1,1 {
02929         # XML Namespace is in scope, prefix supplied
02930         if {[string length $node(node:prefix)]} {
02931         # Make sure that there's a declaration for this XML Namespace
02932         set declPrefix [GetNamespacePrefix $token $node(node:namespaceURI) -prefix $node(node:prefix)]
02933         # ASSERTION: $declPrefix == $node(node:prefix)
02934         set nsPrefix $node(node:prefix):
02935         } elseif {[string length $node(node:namespaceURI)]} {
02936         set nsPrefix [GetNamespacePrefix $token $node(node:namespaceURI)]:
02937         } else {
02938         set nsPrefix {}
02939         }
02940     }
02941 
02942     1,0 {
02943         # XML Namespace is in scope, no prefix
02944         set nsPrefix [GetNamespacePrefix $token $node(node:namespaceURI)]:
02945         if {![string compare $nsPrefix :]} {
02946         set nsPrefix {}
02947         }
02948     }
02949 
02950     0,1 {
02951         # Internal error
02952         set nsPrefix {}
02953     }
02954 
02955     0,0 -
02956     default {
02957         # No XML Namespace is in scope
02958         set nsPrefix {}
02959     }
02960     }
02961     append result <$nsPrefix$node(node:localName)
02962 
02963     append result [Serialize:attributeList [array get $node(element:attributeList)]]
02964 
02965     if {![llength [set $node(node:childNodes)]]} {
02966 
02967     append result />$newline
02968 
02969     } else {
02970 
02971     append result >$newline
02972 
02973     # Do the children
02974     if {[hasmixedcontent $token]} {
02975         set opts(-indent) no
02976     }
02977     append result [eval Serialize:node [list $token] [array get opts]]
02978 
02979     append result [eval Serialize:Indent [array get opts]]
02980     append result "$newline</$nsPrefix$node(node:localName)>$newline"
02981 
02982     }
02983 
02984     return $result
02985 }
02986 
02987 /*  dom::tcl::GetNamespacePrefix --*/
02988 /* */
02989 /*  Determine the XML Namespace prefix for a Namespace URI*/
02990 /* */
02991 /*  Arguments:*/
02992 /*  token   node token*/
02993 /*  nsuri   XML Namespace URI*/
02994 /*  args    configuration options*/
02995 /* */
02996 /*  Results:*/
02997 /*  Returns prefix.*/
02998 /*  May add prefix information to node*/
02999 
03000 ret  dom::tcl::GetNamespacePrefix (type token , type nsuri , type args) {
03001     upvar #0 $token node
03002     array set options $args
03003 
03004     GetNamespaceDecl $token $nsuri declNode prefix
03005 
03006     if {[llength $declNode]} {
03007     # A declaration was found for this Namespace URI
03008     return $prefix
03009     } else {
03010     # No declaration found.  Allocate a prefix
03011     # and add XML Namespace declaration
03012     set prefix {}
03013     catch {set prefix $options(-prefix)}
03014     if {![string compare $prefix {}]} {
03015         upvar #0 [namespace qualifiers $token]::Document document
03016         set prefix ns[incr document(counter)]
03017     }
03018     set node(node:prefix) $prefix
03019     upvar \#0 $node(element:attributeList) attrs
03020     set attrs(${::dom::xmlnsURI}^$prefix) $nsuri
03021 
03022     return $prefix
03023     }
03024 }
03025 
03026 /*  dom::tcl::GetNamespaceDecl --*/
03027 /* */
03028 /*  Find the XML Namespace declaration.*/
03029 /* */
03030 /*  Arguments:*/
03031 /*  token   node token*/
03032 /*  nsuri   XML Namespace URI*/
03033 /*  nodeVar Variable name for declaration*/
03034 /*  prefVar Variable for prefix*/
03035 /* */
03036 /*  Results:*/
03037 /*  If the declaration is found returns node and prefix*/
03038 
03039 ret  dom::tcl::GetNamespaceDecl (type token , type nsuri , type nodeVar , type prefVar) {
03040     upvar #0 $token node
03041     upvar $nodeVar declNode
03042     upvar $prefVar prefix
03043 
03044     while {[string length $node(node:parentNode)]} {
03045 
03046     # Check this node's XML Namespace declarations
03047     catch {unset attrs}
03048     array set attrs [array get $node(element:attributeList)]
03049     foreach {nsdecl decluri} [array get attrs ${::dom::xmlnsURI}^*] {
03050         if {![string compare $decluri $nsuri]} {
03051         regexp [format {%s\^(.*)} $::dom::xmlnsURI] $nsdecl dummy prefix
03052         set declNode $token
03053         return
03054         }
03055     }
03056 
03057     # Move up to parent
03058     set token $node(node:parentNode)
03059     upvar #0 $token node
03060     }
03061 
03062     # Got to Document node and didn't find XML NS decl
03063     set prefix {}
03064     set declNode {}
03065 }
03066 
03067 /*  dom::tcl::Serialize:textNode --*/
03068 /* */
03069 /*  Produce text for a text node.  This procedure may*/
03070 /*  return a CDATA section where appropriate.*/
03071 /* */
03072 /*  Arguments:*/
03073 /*  token   node token*/
03074 /*  args    configuration options*/
03075 /* */
03076 /*  Results:*/
03077 /*  XML format text.*/
03078 
03079 ret  dom::tcl::Serialize:textNode (type token , type args) {
03080     upvar #0 $token node
03081 
03082     if {$node(node:cdatasection)} {
03083     return [Serialize:CDATASection $node(node:nodeValue)]
03084     } elseif {[Serialize:ExceedsThreshold $node(node:nodeValue)]} {
03085     return [Serialize:CDATASection $node(node:nodeValue)]
03086     } else {
03087     return [Encode $node(node:nodeValue)]
03088     }
03089 }
03090 
03091 /*  dom::tcl::Serialize:ExceedsThreshold --*/
03092 /* */
03093 /*  Applies heuristic(s) to determine whether a text node*/
03094 /*  should be formatted as a CDATA section.*/
03095 /* */
03096 /*  Arguments:*/
03097 /*  text    node text*/
03098 /* */
03099 /*  Results:*/
03100 /*  Boolean.*/
03101 
03102 ret  dom::tcl::Serialize:ExceedsThreshold (type text) {
03103     return [expr {[regsub -all {[<>&]} $text {} discard] > $::dom::maxSpecials}]
03104 }
03105 
03106 /*  dom::tcl::Serialize:CDATASection --*/
03107 /* */
03108 /*  Formats a CDATA section.*/
03109 /* */
03110 /*  Arguments:*/
03111 /*  text    node text*/
03112 /* */
03113 /*  Results:*/
03114 /*  XML text.*/
03115 
03116 ret  dom::tcl::Serialize:CDATASection (type text) {
03117     set result {}
03118     while {[regexp {(.*)]]>(.*)} $text discard text trailing]} {
03119     set result \]\]&gt\;<!\[CDATA\[$trailing\]\]>$result
03120     }
03121     return <!\[CDATA\[$text\]\]>$result
03122 }
03123 
03124 /*  dom::tcl::Serialize:processingInstruction --*/
03125 /* */
03126 /*  Produce text for a PI node.*/
03127 /* */
03128 /*  Arguments:*/
03129 /*  token   node token*/
03130 /*  args    configuration options*/
03131 /* */
03132 /*  Results:*/
03133 /*  XML format text.*/
03134 
03135 ret  dom::tcl::Serialize:processingInstruction (type token , type args) {
03136     upvar #0 $token node
03137 
03138     return "[eval Serialize:Indent $args]<?$node(node:nodeName)[expr {$node(node:nodeValue) == "" ? "" : " $node(node:nodeValue)"}]?>"
03139 }
03140 
03141 /*  dom::tcl::Serialize:comment --*/
03142 /* */
03143 /*  Produce text for a comment node.*/
03144 /* */
03145 /*  Arguments:*/
03146 /*  token   node token*/
03147 /*  args    configuration options*/
03148 /* */
03149 /*  Results:*/
03150 /*  XML format text.*/
03151 
03152 ret  dom::tcl::Serialize:comment (type token , type args) {
03153     upvar #0 $token node
03154 
03155     return [eval Serialize:Indent $args]<!--$node(node:nodeValue)-->
03156 }
03157 
03158 /*  dom::tcl::Serialize:entityReference --*/
03159 /* */
03160 /*  Produce text for an entity reference.*/
03161 /* */
03162 /*  Arguments:*/
03163 /*  token   node token*/
03164 /*  args    configuration options*/
03165 /* */
03166 /*  Results:*/
03167 /*  XML format text.*/
03168 
03169 ret  dom::tcl::Serialize:entityReference (type token , type args) {
03170     upvar #0 $token node
03171 
03172     return &$node(node:nodeName)\;
03173 }
03174 
03175 /*  dom::tcl::Encode --*/
03176 /* */
03177 /*  Encode special characters*/
03178 /* */
03179 /*  Arguments:*/
03180 /*  value   text value*/
03181 /* */
03182 /*  Results:*/
03183 /*  XML format text.*/
03184 
03185 ret  dom::tcl::Encode value (
03186     type array , type set , type Entity , optional 
03187     $ =$
03188     < =&lt;
03189     > =&gt;
03190     & =&amp;
03191     \" =&quot;
03192     ' =&apos;
03193     
03194 
03195     , type regsub -, type all , optional ([$<>&"']) $, type value , optional $Entity(\1) , type value
03196 
03197     , type return [, type subst -, type nocommand -, type nobackslash $, type value]
03198 )
03199 
03200 # dom::tcl::Serialize:attributeList --
03201 #
03202 #   Produce text for an attribute list.
03203 #
03204 # Arguments:
03205 #   l   name/value paired list
03206 #
03207 # Results:
03208 #   XML format text.
03209 
03210 proc dom::tcl::Serialize:attributeList {l} {
03211 
03212      result =  {}
03213     foreach {name value} $l {
03214 
03215     if {[regexp {^([^^]+)\^(.*)$} $name discard nsuri prefix]} {
03216         if {[string compare $nsuri $::dom::xmlnsURI]} {
03217         /*  Need the node token to resolve the Namespace URI*/
03218         append result { } ?:$prefix =
03219         } else {
03220         /*  A Namespace declaration*/
03221         append result { } xmlns:$prefix =
03222         }
03223     } else {
03224         append result { } $name =
03225     }
03226 
03227     /*  Handle special characters*/
03228     regsub -all & $value {\&amp;} value
03229     regsub -all < $value {\&lt;} value
03230 
03231     if {![string match *\"* $value]} {
03232         append result \"$value\"
03233     } elseif {![string match *'* $value]} {
03234         append result '$value'
03235     } else {
03236         regsub -all \" $value {\&quot;} value
03237         append result \"$value\"
03238     }
03239 
03240     }
03241 
03242     return $result
03243 }
03244 
03245 /*  dom::tcl::Serialize:Indent --*/
03246 /* */
03247 /*  Calculate the indentation required, if any*/
03248 /* */
03249 /*  Arguments:*/
03250 /*  args    configuration options, which may specify -indent*/
03251 /* */
03252 /*  Results:*/
03253 /*  May return white space*/
03254 
03255 ret  dom::tcl::Serialize:Indent args (
03256     type array , type set , type opts [, type list -, type indentspec $::, type dom::, type indentspec]
03257     , type array , type set , type opts $, type args
03258 
03259     , type if , optional ![info =exists opts(-indent)] =|| \
03260         =[regexp {^false|no|off$ $, type opts(-, type indent)]) {
03261     return {}
03262     }
03263 
03264     if {[regexp {^true|yes|on$} $opts(-indent)]} {
03265     /*  Default indent level is 0*/
03266     return \n
03267     }
03268 
03269     if {!$opts(-indent)} {
03270     return \n
03271     }
03272 
03273      ws =  [format \n%\ [expr $opts(-indent) * [lindex $opts(-indentspec) 0]]s { }]
03274     regsub -all [lindex [lindex $opts(-indentspec) 1] 0] $ws [lindex [lindex $opts(-indentspec) 1] 1] ws
03275 
03276     return $ws
03277 
03278 }
03279 
03280 /* */
03281 /* */
03282 /*  Parsing*/
03283 /* */
03284 /* */
03285 
03286 /*  dom::tcl::ParseElementStart --*/
03287 /* */
03288 /*  Push a new element onto the stack.*/
03289 /* */
03290 /*  Arguments:*/
03291 /*  stateVar    global state array variable*/
03292 /*  name        element name*/
03293 /*  attrList    attribute list*/
03294 /*  args        configuration options*/
03295 /* */
03296 /*  Results:*/
03297 /*  An element is created within the currently open element.*/
03298 
03299 ret  dom::tcl::ParseElementStart (type stateVar , type name , type attrList , type args) {
03300 
03301     upvar #0 $stateVar state
03302     array set opts $args
03303 
03304     # Push namespace declarations
03305     # We need to be able to map namespaceURI's back to prefixes
03306     set nsattrlists {}
03307     catch {
03308     foreach {namespaceURI prefix} $opts(-namespacedecls) {
03309         lappend state(NS:$namespaceURI) $prefix
03310 
03311         # Also, synthesize namespace declaration attributes
03312         # TclXML is a little too clever when it parses them away!
03313 
03314         lappend nsattrlists $prefix $namespaceURI
03315     }
03316     lappend opts(-namespaceattributelists) $::dom::xmlnsURI $nsattrlists
03317 
03318     }
03319 
03320     set nsarg {}
03321     catch {
03322     lappend nsarg -namespace $opts(-namespace)
03323     lappend nsarg -localname $name
03324     lappend nsarg -prefix [lindex $state(NS:$opts(-namespace)) end]
03325     }
03326 
03327     lappend state(current) \
03328     [eval CreateElement [list [lindex $state(current) end] $name $attrList] $nsarg [array get opts -namespaceattributelists]]
03329 
03330     if {[info exists opts(-empty)] && $opts(-empty)} {
03331     # Flag this node as being an empty element
03332     upvar #0 [lindex $state(current) end] node
03333     set node(element:empty) 1
03334     }
03335 
03336     # Temporary: implement -progresscommand here, because of broken parser
03337     if {[string length $state(-progresscommand)]} {
03338     if {!([incr state(progCounter)] % $state(-chunksize))} {
03339         uplevel #0 $state(-progresscommand)
03340     }
03341     }
03342 }
03343 
03344 /*  dom::tcl::ParseElementEnd --*/
03345 /* */
03346 /*  Pop an element from the stack.*/
03347 /* */
03348 /*  Arguments:*/
03349 /*  stateVar    global state array variable*/
03350 /*  name        element name*/
03351 /*  args        configuration options*/
03352 /* */
03353 /*  Results:*/
03354 /*  Currently open element is closed.*/
03355 
03356 ret  dom::tcl::ParseElementEnd (type stateVar , type name , type args) {
03357     upvar #0 $stateVar state
03358 
03359     set state(current) [lreplace $state(current) end end]
03360 }
03361 
03362 /*  dom::tcl::ParseCharacterData --*/
03363 /* */
03364 /*  Add a textNode to the currently open element.*/
03365 /* */
03366 /*  Arguments:*/
03367 /*  stateVar    global state array variable*/
03368 /*  data        character data*/
03369 /* */
03370 /*  Results:*/
03371 /*  A textNode is created.*/
03372 
03373 ret  dom::tcl::ParseCharacterData (type stateVar , type data) {
03374     upvar #0 $stateVar state
03375 
03376     CreateTextNode [lindex $state(current) end] $data
03377 }
03378 
03379 /*  dom::tcl::ParseProcessingInstruction --*/
03380 /* */
03381 /*  Add a PI to the currently open element.*/
03382 /* */
03383 /*  Arguments:*/
03384 /*  stateVar    global state array variable*/
03385 /*  name        PI name*/
03386 /*  target      PI target*/
03387 /* */
03388 /*  Results:*/
03389 /*  A processingInstruction node is created.*/
03390 
03391 ret  dom::tcl::ParseProcessingInstruction (type stateVar , type name , type target) {
03392     upvar #0 $stateVar state
03393 
03394     CreateGeneric [lindex $state(current) end] node:nodeType processingInstruction node:nodeName $name node:nodeValue $target
03395 }
03396 
03397 /*  dom::tcl::ParseXMLDeclaration --*/
03398 /* */
03399 /*  Add information from the XML Declaration to the document.*/
03400 /* */
03401 /*  Arguments:*/
03402 /*  stateVar    global state array variable*/
03403 /*  version     version identifier*/
03404 /*  encoding    character encoding*/
03405 /*  standalone  standalone document declaration*/
03406 /* */
03407 /*  Results:*/
03408 /*  Document node modified.*/
03409 
03410 ret  dom::tcl::ParseXMLDeclaration (type stateVar , type version , type encoding , type standalone) {
03411     upvar #0 $stateVar state
03412 
03413     upvar #0 $state(docNode) document
03414     array set xmldecl $document(document:xmldecl)
03415 
03416     array set xmldecl [list version $version    \
03417         standalone $standalone      \
03418         encoding $encoding          \
03419     ]
03420 
03421     set document(document:xmldecl) [array get xmldecl]
03422 
03423     return {}
03424 }
03425 
03426 /*  dom::tcl::ParseDocType --*/
03427 /* */
03428 /*  Add a Document Type Declaration node to the document.*/
03429 /* */
03430 /*  Arguments:*/
03431 /*  stateVar    global state array variable*/
03432 /*  root        root element type*/
03433 /*  publit      public identifier literal*/
03434 /*  systemlist  system identifier literal*/
03435 /*  dtd     internal DTD subset*/
03436 /* */
03437 /*  Results:*/
03438 /*  DocType node added*/
03439 
03440 ret  dom::tcl::ParseDocType (type stateVar , type root , optional publit ={) {systemlit {}} {dtd {}} args} {
03441     upvar /* 0 $stateVar state*/
03442     upvar /* 0 $state(docNode) document*/
03443 
03444      document = (document:doctype) [CreateDocType $state(docNode) $publit $systemlit $dtd]
03445 
03446     return {}
03447 }
03448 
03449 /*  dom::tcl::ParseComment --*/
03450 /* */
03451 /*  Parse comment*/
03452 /* */
03453 /*  Arguments:*/
03454 /*  stateVar    state array*/
03455 /*  data        comment data*/
03456 /* */
03457 /*  Results:*/
03458 /*  Comment node added to DOM tree*/
03459 
03460 ret  dom::tcl::ParseComment (type stateVar , type data) {
03461     upvar #0 $stateVar state
03462 
03463     CreateGeneric [lindex $state(current) end] node:nodeType comment node:nodeValue $data
03464 
03465     return {}
03466 }
03467 
03468 /*  dom::tcl::ParseEntityReference --*/
03469 /* */
03470 /*  Parse an entity reference*/
03471 /* */
03472 /*  Arguments:*/
03473 /*  stateVar    state variable*/
03474 /*  ref     entity*/
03475 /* */
03476 /*  Results:*/
03477 /*  Entity reference node added to DOM tree*/
03478 
03479 ret  dom::tcl::ParseEntityReference (type stateVar , type ref) {
03480     upvar #0 $stateVar state
03481 
03482     CreateGeneric [lindex $state(current) end] node:nodeType entityReference node:nodeName $ref
03483 
03484     return {}
03485 }
03486 
03487 /* */
03488 /* */
03489 /*  Trim white space*/
03490 /* */
03491 /* */
03492 
03493 /*  dom::tcl::Trim --*/
03494 /* */
03495 /*  Remove textNodes that only contain white space*/
03496 /* */
03497 /*  Arguments:*/
03498 /*  nodeid  node to trim*/
03499 /* */
03500 /*  Results:*/
03501 /*  textNode nodes may be removed (from descendants)*/
03502 
03503 ret  dom::tcl::Trim nodeid (
03504     type upvar #0 $, type nodeid , type node
03505 
03506     , type switch $, type node(, type node:, type nodeType) , optional 
03507 
03508     textNode ={
03509         if ={![string length =[string trim =$node(node:nodeValue)]] , optional 
03510         node =removeChild $node(node:parentNode) =$nodeid
03511         
03512     )
03513 
03514     default {
03515         # Some nodes have no child list.  Reported by Jim Hollister <jhollister@objectspace.com>
03516         set children {}
03517         catch {set children [set $node(node:childNodes)]}
03518         foreach child $children {
03519         Trim $child
03520         }
03521     }
03522 
03523     }
03524 
03525     return {}
03526 }
03527 
03528 /* */
03529 /* */
03530 /*  XPath support*/
03531 /* */
03532 /* */
03533 
03534 /*  dom::tcl::XPath:CreateNode --*/
03535 /* */
03536 /*  Given an XPath expression, create the node*/
03537 /*  referred to by the expression.  Nodes required*/
03538 /*  as steps of the path are created if they do*/
03539 /*  not exist.*/
03540 /* */
03541 /*  Arguments:*/
03542 /*  node    context node*/
03543 /*  path    location path*/
03544 /* */
03545 /*  Results:*/
03546 /*  Node(s) created in the DOM tree.*/
03547 /*  Returns token for deepest node in the expression.*/
03548 
03549 ret  dom::tcl::XPath:CreateNode (type node , type path) {
03550 
03551     set root [::dom::node cget $node -ownerDocument]
03552 
03553     set spath [::xpath::split $path]
03554 
03555     if {[llength $spath] <= 1} {
03556     # / - do nothing
03557     return $root
03558     }
03559 
03560     if {![llength [lindex $spath 0]]} {
03561     # Absolute location path
03562     set context $root
03563     set spath [lrange $spath 1 end]
03564     set contexttype document
03565     } else {
03566     set context $node
03567     set contexttype [::dom::node cget $node -nodeType]
03568     }
03569 
03570     foreach step $spath {
03571 
03572     # Sanity check on path
03573     switch $contexttype {
03574         document -
03575         documentFragment -
03576         element {}
03577         default {
03578         return -code error "node type \"$contexttype\" have no children"
03579         }
03580     }
03581 
03582     switch [lindex $step 0] {
03583 
03584         child {
03585         if {[llength [lindex $step 1]] > 1} {
03586             foreach {nodetype discard} [lindex $step 1] break
03587 
03588             switch -- $nodetype {
03589             text {
03590                 set posn [CreateNode:FindPosition [lindex $step 2]]
03591 
03592                 set count 0
03593                 set targetNode {}
03594                 foreach child [::dom::node children $context] {
03595                 switch [::dom::node cget $child -nodeType] {
03596                     textNode {
03597                     incr count
03598                     if {$count == $posn} {
03599                         set targetNode $child
03600                         break
03601                     }
03602                     }
03603                     default {}
03604                 }
03605                 }
03606 
03607                 if {[string length $targetNode]} {
03608                 set context $targetNode
03609                 } else {
03610                 # Creating sequential textNodes doesn't make sense
03611                 set context [::dom::document createTextNode $context {}]
03612                 }
03613                 set contexttype textNode
03614             }
03615             default {
03616                 return -code error "node type test \"${nodetype}()\" not supported"
03617             }
03618             }
03619         } else {
03620             # Find the child element
03621             set posn [CreateNode:FindPosition [lindex $step 2]]
03622 
03623             set count 0
03624             set targetNode {}
03625             foreach child [::dom::node children $context] {
03626             switch [node cget $child -nodeType] {
03627                 element {
03628                 if {![string compare [lindex $step 1] [::dom::node cget $child -nodeName]]} {
03629                     incr count
03630                     if {$count == $posn} {
03631                     set targetNode $child
03632                     break
03633                     }
03634                 }
03635                 }
03636                 default {}
03637             }
03638             }
03639 
03640             if {[string length $targetNode]} {
03641             set context $targetNode
03642             } else {
03643             # Didn't find it so create required elements
03644             while {$count < $posn} {
03645                 set child [::dom::document createElement $context [lindex $step 1]]
03646                 incr count
03647             }
03648             set context $child
03649             }
03650             set contexttype element
03651 
03652         }
03653         }
03654 
03655         default {
03656         return -code error "axis \"[lindex $step 0]\" is not supported"
03657         }
03658     }
03659     }
03660 
03661     return $context
03662 }
03663 
03664 /*  dom::tcl::CreateNode:FindPosition --*/
03665 
03666 ret  dom::tcl::CreateNode:FindPosition predicates (
03667     type switch [, type llength $, type predicates] , optional 
03668     0 ={
03669         return =1
03670     
03671     1 , optional 
03672         # =Fall-through
03673     
03674     , type default , optional 
03675         return =-code error ="multiple predicates =not yet =supported"
03676     
03677     )
03678     set predicate [lindex $predicates 0]
03679 
03680     switch -- [lindex [lindex $predicate 0] 0] {
03681     function {
03682         switch -- [lindex [lindex $predicate 0] 1] {
03683         position {
03684             if {[lindex $predicate 1] == "="} {
03685             if {[string compare [lindex [lindex $predicate 2] 0] "number"]} {
03686                 return -code error "operand must be a number"
03687             } else {
03688                 set posn [lindex [lindex $predicate 2] 1]
03689             }
03690             } else {
03691             return -code error "operator must be \"=\""
03692             }
03693         }
03694         default {
03695             return -code error "predicate function \"[lindex [lindex $predicate 0] 1]\" not supported"
03696         }
03697         }
03698     }
03699     default {
03700         return -code error "predicate must be position() function"
03701     }
03702     }
03703 
03704     return $posn
03705 }
03706 
03707 /*  dom::tcl::XPath:SelectNode --*/
03708 /* */
03709 /*  Match nodes with an XPath location path*/
03710 /* */
03711 /*  Arguments:*/
03712 /*  ctxt    context - Tcl list*/
03713 /*  path    location path*/
03714 /* */
03715 /*  Results:*/
03716 /*  Returns Tcl list of matching nodes*/
03717 
03718 ret  dom::tcl::XPath:SelectNode (type ctxt , type path) {
03719 
03720     if {![llength $ctxt]} {
03721     return {}
03722     }
03723 
03724     set spath [xpath::split $path]
03725 
03726     if {[string length [node parent [lindex $ctxt 0]]]} {
03727     set root [namespace qualifiers [lindex $ctxt 0]]::Document
03728     } else {
03729     set root [lindex $ctxt 0]
03730     }
03731 
03732     if {[llength $spath] == 0} {
03733     return $root
03734     }
03735     if {[llength $spath] == 1 && [llength [lindex $spath 0]] == 0} {
03736     return $root
03737     }
03738 
03739     if {![llength [lindex $spath 0]]} {
03740     set ctxt $root
03741     set spath [lrange $spath 1 end]
03742     }
03743 
03744     return [XPath:SelectNode:Rel $ctxt $spath]
03745 }
03746 
03747 /*  dom::tcl::XPath:SelectNode:Rel --*/
03748 /* */
03749 /*  Match nodes with an XPath location path*/
03750 /* */
03751 /*  Arguments:*/
03752 /*  ctxt    context - Tcl list*/
03753 /*  path    split location path*/
03754 /* */
03755 /*  Results:*/
03756 /*  Returns Tcl list of matching nodes*/
03757 
03758 ret  dom::tcl::XPath:SelectNode:Rel (type ctxt , type spath) {
03759     if {![llength $spath]} {
03760     return $ctxt
03761     }
03762 
03763     set step [lindex $spath 0]
03764     set result {}
03765     switch [lindex $step 0] {
03766 
03767     child {
03768         # All children are candidates
03769         set children {}
03770         foreach node [XPath:SN:GetElementTypeNodes $ctxt] {
03771         eval lappend children [node children $node]
03772         }
03773 
03774         # Now apply node test to each child
03775         foreach node $children {
03776         if {[XPath:SN:ApplyNodeTest $node [lindex $step 1]]} {
03777             lappend result $node
03778         }
03779         }
03780 
03781     }
03782 
03783     descendant-or-self {
03784         foreach node $ctxt {
03785         if {[XPath:SN:ApplyNodeTest $node [lindex $step 1]]} {
03786             lappend result $node
03787         }
03788         eval lappend result [XPath:SN:DescendAndTest [node children $node] [lindex $step 1]]
03789         }
03790     }
03791 
03792     descendant {
03793         foreach node $ctxt {
03794         eval lappend result [XPath:SN:DescendAndTest [node children $node] [lindex $step 1]]
03795         }
03796     }
03797 
03798     attribute {
03799         if {[string compare [lindex $step 1] "*"]} {
03800         foreach node $ctxt {
03801             set attrNode [element getAttributeNode $node [lindex $step 1]]
03802             if {[llength $attrNode]} {
03803             lappend result $attrNode
03804             }
03805         }
03806         } else {
03807         # All attributes are returned
03808         foreach node $ctxt {
03809             foreach attrName [array names [node cget $node -attributes]] {
03810             set attrNode [element getAttributeNode $node $attrName]
03811             if {[llength $attrNode]} {
03812                 lappend result $attrNode
03813             }
03814             }
03815         }
03816         }
03817     }
03818 
03819     default {
03820         return -code error "axis \"[lindex $step 0]\" is not supported"
03821     }
03822     }
03823 
03824     # Now apply predicates
03825     set result [XPath:ApplyPredicates $result [lindex $step 2]]
03826 
03827     # Apply the next location step
03828     return [XPath:SelectNode:Rel $result [lrange $spath 1 end]]
03829 }
03830 
03831 /*  dom::tcl::XPath:SN:GetElementTypeNodes --*/
03832 /* */
03833 /*  Reduce nodeset to those nodes of element type*/
03834 /* */
03835 /*  Arguments:*/
03836 /*  nodeset set of nodes*/
03837 /* */
03838 /*  Results:*/
03839 /*  Returns nodeset in which all nodes are element type*/
03840 
03841 ret  dom::tcl::XPath:SN:GetElementTypeNodes nodeset (
03842     type set , type result , optional 
03843     , type foreach , type node $, type nodeset , optional 
03844     switch =[node cget =$node -nodeType] ={
03845         document =-
03846         documentFragment =-
03847         element ={
03848         lappend =result $node
03849         
03850         , type default , optional 
03851     )
03852     }
03853     return $result
03854 }
03855 
03856 # dom::tcl::XPath:SN:ApplyNodeTest --
03857 #
03858 #   Apply the node test to a node
03859 #
03860 # Arguments:
03861 #   node    DOM node to test
03862 #   test    node test
03863 #
03864 # Results:
03865 #   1 if node passes, 0 otherwise
03866 
03867 proc dom::tcl::XPath:SN:ApplyNodeTest {node test} {
03868     if {[llength $test] > 1} {
03869     foreach {name typetest} $test break
03870     /*  Node type test*/
03871     switch -glob -- $name,[node cget $node -nodeType] {
03872         node,* {
03873         return 1
03874         }
03875         text,textNode -
03876         comment,comment -
03877         ret essing-instruction,processingInstruction (
03878         type return 1
03879         )
03880         text,* -
03881         comment,* -
03882         processing-instruction,* {
03883         return 0
03884         }
03885         default {
03886         return -code error "illegal node type test \"[lindex $step 1]\""
03887         }
03888     }
03889     } else {
03890     /*  Node name test*/
03891     switch -glob -- $test,[node cget $node -nodeType],[node cget $node -nodeName] \
03892         \\*,element,* {
03893         return 1
03894     } \
03895         \\*,* {
03896         return 0
03897     } \
03898         *,element,$test {
03899         return 1
03900     }
03901     }
03902 
03903     return 0
03904 }
03905 
03906 /*  dom::tcl::XPath:SN:DescendAndTest --*/
03907 /* */
03908 /*  Descend the element hierarchy,*/
03909 /*  apply the node test as we go*/
03910 /* */
03911 /*  Arguments:*/
03912 /*  nodeset nodes to be tested and descended*/
03913 /*  test    node test*/
03914 /* */
03915 /*  Results:*/
03916 /*  Returned nodeset of nodes which pass the test*/
03917 
03918 ret  dom::tcl::XPath:SN:DescendAndTest (type nodeset , type test) {
03919     set result {}
03920 
03921     foreach node $nodeset {
03922     if {[XPath:SN:ApplyNodeTest $node $test]} {
03923         lappend result $node
03924     }
03925     switch [node cget $node -nodeType] {
03926         document -
03927         documentFragment -
03928         element {
03929         eval lappend result [XPath:SN:DescendAndTest [node children $node] $test]
03930         }
03931     }
03932     }
03933 
03934     return $result
03935 }
03936 
03937 /*  dom::tcl::XPath:ApplyPredicates --*/
03938 /* */
03939 /*  Filter a nodeset with predicates*/
03940 /* */
03941 /*  Arguments:*/
03942 /*  ctxt    current context nodeset*/
03943 /*  preds   list of predicates*/
03944 /* */
03945 /*  Results:*/
03946 /*  Returns new (possibly reduced) context nodeset*/
03947 
03948 ret  dom::tcl::XPath:ApplyPredicates (type ctxt , type preds) {
03949 
03950     set result {}
03951     foreach node $ctxt {
03952     set passed 1
03953     foreach predicate $preds {
03954         if {![XPath:ApplyPredicate $node $predicate]} {
03955         set passed 0
03956         break
03957         }
03958     }
03959     if {$passed} {
03960         lappend result $node
03961     }
03962     }
03963 
03964     return $result
03965 }
03966 
03967 /*  dom::tcl::XPath:ApplyPredicate --*/
03968 /* */
03969 /*  Filter a node with a single predicate*/
03970 /* */
03971 /*  Arguments:*/
03972 /*  node    current context node*/
03973 /*  pred    predicate*/
03974 /* */
03975 /*  Results:*/
03976 /*  Returns boolean*/
03977 
03978 ret  dom::tcl::XPath:ApplyPredicate (type node , type pred) {
03979 
03980     switch -- [lindex $pred 0] {
03981     = -
03982     != -
03983     >= -
03984     <= -
03985     > -
03986     > {
03987 
03988         if {[llength $pred] != 3} {
03989         return -code error "malformed expression"
03990         }
03991 
03992         set operand1 [XPath:Pred:ResolveExpr $node [lindex $pred 1]]
03993         set operand2 [XPath:Pred:ResolveExpr $node [lindex $pred 2]]
03994 
03995         # Convert operands to the correct type, if necessary
03996         switch -glob [lindex $operand1 0],[lindex $operand2 0] {
03997         literal,literal {
03998             return [XPath:Pred:CompareLiterals [lindex $pred 0] [lindex $operand1 1] [lindex $operand2 1]]
03999         }
04000 
04001         number,number -
04002         literal,number -
04003         number,literal {
04004             # Compare as numbers
04005             return [XPath:Pred:CompareNumbers [lindex $pred 0] [lindex $operand1 1] [lindex $operand2 1]]
04006         }
04007 
04008         boolean,boolean {
04009             # Compare as booleans
04010             return -code error "boolean comparison not yet implemented"
04011         }
04012 
04013         node,node {
04014             # Nodeset comparison
04015             return -code error "nodeset comparison not yet implemented"
04016         }
04017 
04018         node,* {
04019             set value {}
04020             if {[llength [lindex $operand1 1]]} {
04021             set value [node stringValue [lindex [lindex $operand1 1] 0]]
04022             }
04023             return [XPath:Pred:CompareLiterals [lindex $pred 0] $value [lindex $operand2 1]]
04024         }
04025         *,node {
04026             set value {}
04027             if {[llength [lindex $operand2 1]]} {
04028             set value [node stringValue [lindex [lindex $operand2 1] 0]]
04029             }
04030             return [XPath:Pred:CompareLiterals [lindex $pred 0] $value [lindex $operand1 1]]
04031         }
04032 
04033         default {
04034             return -code error "can't compare [lindex $operand1 0] to [lindex $operand2 0]"
04035         }
04036         }
04037     }
04038 
04039     function {
04040         return -code error "invalid predicate"
04041     }
04042     number -
04043     literal {
04044         return -code error "invalid predicate"
04045     }
04046 
04047     path {
04048         set nodeset [XPath:SelectNode:Rel $node [lindex $pred 1]]
04049         return [expr {[llength $nodeset] > 0 ? 1 : 0}]
04050     }
04051 
04052     }
04053 
04054     return 1
04055 }
04056 
04057 /*  dom::tcl::XPath:Pred:Compare --*/
04058 
04059 ret  dom::tcl::XPath:Pred:CompareLiterals (type op , type operand1 , type operand2) {
04060     set result [string compare $operand1 $operand2]
04061 
04062     # The obvious:
04063     #return [expr {$result $opMap($op) 0}]
04064     # doesn't compile
04065     
04066     switch $op {
04067     = {
04068         return [expr {$result == 0}]
04069     }
04070     != {
04071         return [expr {$result != 0}]
04072     }
04073     <= {
04074         return [expr {$result <= 0}]
04075     }
04076     >= {
04077         return [expr {$result >= 0}]
04078     }
04079     < {
04080         return [expr {$result < 0}]
04081     }
04082     > {
04083         return [expr {$result > 0}]
04084     }
04085     }
04086     return -code error "internal error"
04087 }
04088 
04089 /*  dom::tcl::XPath:Pred:ResolveExpr --*/
04090 
04091 ret  dom::tcl::XPath:Pred:ResolveExpr (type node , type expr) {
04092 
04093     switch [lindex $expr 0] {
04094     path {
04095         return [list node [XPath:SelectNode:Rel $node [lindex $expr 1]]]
04096     }
04097 
04098     function -
04099     group {
04100         return -code error "[lindex $expr 0] not yet implemented"
04101     }
04102     literal -
04103     number -
04104     boolean {
04105         return $expr
04106     }
04107 
04108     default {
04109         return -code error "internal error"
04110     }
04111     }
04112 
04113     return {}
04114 }
04115 
04116 /* */
04117 /* */
04118 /*  Miscellaneous*/
04119 /* */
04120 /* */
04121 
04122 /*  dom::tcl::hasmixedcontent --*/
04123 /* */
04124 /*  Determine whether an element contains mixed content*/
04125 /* */
04126 /*  Arguments:*/
04127 /*  token   dom node*/
04128 /* */
04129 /*  Results:*/
04130 /*  Returns 1 if element contains mixed content,*/
04131 /*  0 otherwise*/
04132 
04133 ret  dom::tcl::hasmixedcontent token (
04134     type upvar #0 $, type token , type node
04135 
04136     , type if , optional [string =compare $node(node:nodeType) ="element"] , optional 
04137     # =Really undefined
04138     =return 0
04139     
04140 
04141     , type foreach , type child [, type set $, type node(, type node:, type childNodes)] , optional 
04142     upvar =#0 $child =childnode
04143     if ={![string compare =$childnode(node:nodeType) "textNode"] , optional 
04144         return =1
04145     
04146     )
04147 
04148     return 0
04149 }
04150 
04151 # dom::tcl::prefix2namespaceURI --
04152 #
04153 #   Given an XML Namespace prefix, find the corresponding Namespace URI
04154 #
04155 # Arguments:
04156 #   node    DOM Node
04157 #   prefix  XML Namespace prefix
04158 #
04159 # Results:
04160 #   Returns URI
04161 
04162 proc dom::tcl::prefix2namespaceURI {node prefix} {
04163 
04164     # Search this node and its ancestors for the appropriate
04165     # XML Namespace declaration
04166 
04167     set parent [dom::node parent $node]
04168     set nsuri [dom::element getAttributeNS $node $::dom::xmlnsURI $prefix]
04169     if {[string length $parent] && ![string length $nsuri]} {
04170      nsuri =  [dom::element getAttributeNS $parent $::dom::xmlnsURI $prefix]
04171      parent =  [dom::node parent $parent]
04172     }
04173 
04174     if {[string length $nsuri]} {
04175     return $nsuri
04176     } else {
04177     return -code error "unable to find namespace URI for prefix \"$prefix\""
04178     }
04179 
04180 }
04181 
04182 /*  dom::tcl::namespaceURI2prefix --*/
04183 /* */
04184 /*  Given an XML Namespace URI, find the corresponding prefix*/
04185 /* */
04186 /*  Arguments:*/
04187 /*  node    DOM Node*/
04188 /*  nsuri   XML Namespace URI*/
04189 /* */
04190 /*  Results:*/
04191 /*  Returns prefix*/
04192 
04193 ret  dom::tcl::namespaceURI2prefix (type node , type nsuri) {
04194 
04195     # Search this node and its ancestors for the desired
04196     # XML Namespace declaration
04197 
04198     set found 0
04199     set prefix {}
04200     set parent [dom::node parent $node]
04201     while {[string length $parent]} {
04202     upvar #0 $node nodeinfo
04203     catch {unset attrs}
04204     array set attrs [array get $nodeinfo(element:attributeList)]
04205     foreach {nsdecl declNSuri} [array get attrs ${::dom::xmlnsURI}^*] {
04206         if {![string compare $declNSuri $nsuri]} {
04207         set found 1
04208         set prefix [lindex [split $nsdecl ^] 1]
04209         break
04210         }
04211     }
04212     if {$found} {
04213         break
04214     }
04215     set node $parent
04216     set parent [dom::node parent $node]
04217     }
04218 
04219     if {$found} {
04220     return $prefix
04221     } else {
04222     return -code error "unable to find prefix for namespace URI \"$nsuri\""
04223     }
04224 
04225 }
04226 
04227 /*  dom::tcl::GetField --*/
04228 /* */
04229 /*  Return a value, or empty string if not defined*/
04230 /* */
04231 /*  Arguments:*/
04232 /*  var name of variable to return*/
04233 /* */
04234 /*  Results:*/
04235 /*  Returns the value, or empty string if variable is not defined.*/
04236 
04237 ret  dom::tcl::GetField var (
04238     type upvar $, type var , type v
04239     , type if , optional [info =exists v] , optional 
04240     return =$v
04241      , type else , optional 
04242     return ={
04243     )
04244 }
04245 
04246 # dom::tcl::Min --
04247 #
04248 #   Return the minimum of two numeric values
04249 #
04250 # Arguments:
04251 #   a   a value
04252 #   b   another value
04253 #
04254 # Results:
04255 #   Returns the value which is lower than the other.
04256 
04257 proc dom::tcl::Min {a b} {
04258     return [expr {$a < $b ? $a : $b}]
04259 }
04260 
04261 /*  dom::tcl::Max --*/
04262 /* */
04263 /*  Return the maximum of two numeric values*/
04264 /* */
04265 /*  Arguments:*/
04266 /*  a   a value*/
04267 /*  b   another value*/
04268 /* */
04269 /*  Results:*/
04270 /*  Returns the value which is greater than the other.*/
04271 
04272 ret  dom::tcl::Max (type a , type b) {
04273     return [expr {$a > $b ? $a : $b}]
04274 }
04275 
04276 /*  dom::tcl::Boolean --*/
04277 /* */
04278 /*  Return a boolean value*/
04279 /* */
04280 /*  Arguments:*/
04281 /*  b   value*/
04282 /* */
04283 /*  Results:*/
04284 /*  Returns 0 or 1*/
04285 
04286 ret  dom::tcl::Boolean b (
04287     type regsub -, type nocase , optional ^(true|yes|1|on)$ $, type b 1 , type b
04288     , type regsub -, type nocase , optional ^(false|no|0|off)$ $, type b 0 , type b
04289     , type return $, type b
04290 )
04291 
04292 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1