00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 package require xml 3.0
00019
00020 package provide dom::tcl 3.0
00021
00022
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
00044
00045 variable indentspec [list 2 [list { } \t]]
00046
00047
00048 variable xmlnsURI http:
00049
00050
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
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
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
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
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
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830
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
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
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
00982
00983
00984
00985
00986
00987
00988
00989
00990
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
01037
01038
01039
01040
01041
01042
01043
01044
01045
01046
01047
01048
01049
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
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
01157
01158
01159
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169 ret dom::tcl::DocumentType:Delete (type name , type old , type new , type op) {
01170 DOMImplementation destroy $name
01171 }
01172
01173
01174
01175
01176
01177
01178
01179
01180
01181
01182
01183
01184
01185
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
01192
01193
01194 if {$::dom::strictDOM} {
01195 append nodeOptionsRO |nodeName
01196 } else {
01197 append nodeOptionsRW |nodeName
01198 }
01199 }
01200
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
01826
01827
01828
01829
01830
01831
01832
01833
01834
01835
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
01874
01875
01876
01877
01878
01879
01880
01881
01882
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
01895
01896
01897
01898
01899
01900
01901
01902
01903
01904
01905
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
01916
01917
01918
01919
01920
01921
01922
01923
01924
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
01956
01957
01958
01959
01960
01961
01962
01963
01964
01965
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
02174
02175
02176
02177
02178
02179
02180
02181
02182
02183
02184
02185
02186
02187
02188
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
02227
02228
02229
02230
02231
02232
02233
02234
02235
02236
02237
02238
02239
02240
02241
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
02272
02273
02274
02275
02276
02277
02278
02279
02280
02281
02282
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
02289
02290
02291
02292
02293
02294
02295
02296
02297
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
02352
02353
02354
02355
02356
02357
02358
02359
02360
02361
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
02446
02447
02448
02449
02450
02451
02452
02453
02454
02455
02456
02457
02458
02459
02460
02461
02462
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
02469
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
02671
02672
02673
02674
02675
02676
02677
02678
02679
02680
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
02719
02720
02721
02722
02723
02724
02725
02726
02727
02728
02729
02730
02731
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
02749
02750
02751
02752
02753
02754
02755
02756
02757
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
02816
02817
02818
02819
02820
02821
02822
02823
02824
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
02845
02846
02847
02848
02849
02850
02851
02852
02853
02854
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
02868
02869
02870
02871
02872
02873
02874
02875
02876
02877
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
02904
02905
02906
02907
02908
02909
02910
02911
02912
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
02988
02989
02990
02991
02992
02993
02994
02995
02996
02997
02998
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
03027
03028
03029
03030
03031
03032
03033
03034
03035
03036
03037
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
03068
03069
03070
03071
03072
03073
03074
03075
03076
03077
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
03092
03093
03094
03095
03096
03097
03098
03099
03100
03101
03102 ret dom::tcl::Serialize:ExceedsThreshold (type text) {
03103 return [expr {[regsub -all {[<>&]} $text {} discard] > $::dom::maxSpecials}]
03104 }
03105
03106
03107
03108
03109
03110
03111
03112
03113
03114
03115
03116 ret dom::tcl::Serialize:CDATASection (type text) {
03117 set result {}
03118 while {[regexp {(.*)]]>(.*)} $text discard text trailing]} {
03119 set result \]\]>\;<!\[CDATA\[$trailing\]\]>$result
03120 }
03121 return <!\[CDATA\[$text\]\]>$result
03122 }
03123
03124
03125
03126
03127
03128
03129
03130
03131
03132
03133
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
03142
03143
03144
03145
03146
03147
03148
03149
03150
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
03159
03160
03161
03162
03163
03164
03165
03166
03167
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
03176
03177
03178
03179
03180
03181
03182
03183
03184
03185 ret dom::tcl::Encode value (
03186 type array , type set , type Entity , optional
03187 $ =$
03188 < =<
03189 > =>
03190 & =&
03191 \" ="
03192 ' ='
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 {\&} value
03229 regsub -all < $value {\<} 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 {\"} 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
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
03708
03709
03710
03711
03712
03713
03714
03715
03716
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
03748
03749
03750
03751
03752
03753
03754
03755
03756
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
03832
03833
03834
03835
03836
03837
03838
03839
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
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
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
03907
03908
03909
03910
03911
03912
03913
03914
03915
03916
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
03938
03939
03940
03941
03942
03943
03944
03945
03946
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
03968
03969
03970
03971
03972
03973
03974
03975
03976
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
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
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
04119
04120
04121
04122
04123
04124
04125
04126
04127
04128
04129
04130
04131
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
04183
04184
04185
04186
04187
04188
04189
04190
04191
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
04228
04229
04230
04231
04232
04233
04234
04235
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
04262
04263
04264
04265
04266
04267
04268
04269
04270
04271
04272 ret dom::tcl::Max (type a , type b) {
04273 return [expr {$a > $b ? $a : $b}]
04274 }
04275
04276
04277
04278
04279
04280
04281
04282
04283
04284
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