| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 3 |  |  | 3 |  | 70707 | use strict; | 
|  | 3 |  |  |  |  | 7 |  | 
|  | 3 |  |  |  |  | 138 |  | 
| 2 | 3 |  |  | 3 |  | 21 | use warnings; | 
|  | 3 |  |  |  |  | 4 |  | 
|  | 3 |  |  |  |  | 126 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | package XHTML::Instrumented; | 
| 5 |  |  |  |  |  |  |  | 
| 6 | 3 |  |  | 3 |  | 1654 | use XHTML::Instrumented::Entry; | 
|  | 3 |  |  |  |  | 9 |  | 
|  | 3 |  |  |  |  | 96 |  | 
| 7 | 3 |  |  | 3 |  | 2208 | use XHTML::Instrumented::Context; | 
|  | 3 |  |  |  |  | 9 |  | 
|  | 3 |  |  |  |  | 99 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 3 |  |  | 3 |  | 19 | use Carp qw (croak verbose); | 
|  | 3 |  |  |  |  | 7 |  | 
|  | 3 |  |  |  |  | 464 |  | 
| 10 | 3 |  |  | 3 |  | 2943 | use XML::Parser; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | =head1 NAME | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | XHTML::Instrumented - packages to control XHTML | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | =head1 VERSION | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | Version 0.092 | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | =cut | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | our $VERSION = '0.092'; | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | our @CARP_NOT = ( 'XML::Parser::Expat' ); | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | use Params::Validate qw( validate SCALAR SCALARREF BOOLEAN HASHREF OBJECT UNDEF CODEREF ); | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | our $path = '.'; | 
| 29 |  |  |  |  |  |  | our $cachepath; | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | sub path | 
| 32 |  |  |  |  |  |  | { | 
| 33 |  |  |  |  |  |  | my $self = shift; | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | $self->{path} || $path; | 
| 36 |  |  |  |  |  |  | } | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | sub cachepath | 
| 39 |  |  |  |  |  |  | { | 
| 40 |  |  |  |  |  |  | my $self = shift; | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | $self->{cachepath} || $cachepath || $self->path; | 
| 43 |  |  |  |  |  |  | } | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | sub cachefilename | 
| 46 |  |  |  |  |  |  | { | 
| 47 |  |  |  |  |  |  | my $self = shift; | 
| 48 |  |  |  |  |  |  | my $file = $self->cachepath; | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | if ($self->{type} || $self->{default_type}) { | 
| 51 |  |  |  |  |  |  | $file .= '/' . $self->{type} || $self->{default_type} if $self->{type} || $self->{default_type}; | 
| 52 |  |  |  |  |  |  | $file .= '/' . $self->{name}; | 
| 53 |  |  |  |  |  |  | $file .= '.cxi'; | 
| 54 |  |  |  |  |  |  | } elsif ($self->{name}) { | 
| 55 |  |  |  |  |  |  | $file .= '/' . $self->{name} . '.cxi'; | 
| 56 |  |  |  |  |  |  | } else { | 
| 57 |  |  |  |  |  |  | $file = $self->{filename} . '.cxi'; | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | return $file; | 
| 61 |  |  |  |  |  |  | } | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | sub import | 
| 64 |  |  |  |  |  |  | { | 
| 65 |  |  |  |  |  |  | my $class = shift; | 
| 66 |  |  |  |  |  |  | my %p = validate(@_, { | 
| 67 |  |  |  |  |  |  | path => 0, | 
| 68 |  |  |  |  |  |  | cachepath => 0, | 
| 69 |  |  |  |  |  |  | }); | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | $path = $p{path}; | 
| 72 |  |  |  |  |  |  | $cachepath = $p{cachepath}; | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | sub new | 
| 76 |  |  |  |  |  |  | { | 
| 77 |  |  |  |  |  |  | my $class = shift; | 
| 78 |  |  |  |  |  |  | my $self = bless { validate(@_, { | 
| 79 |  |  |  |  |  |  | 'name' => { | 
| 80 |  |  |  |  |  |  | type => SCALAR | SCALARREF, | 
| 81 |  |  |  |  |  |  | optional => 1, | 
| 82 |  |  |  |  |  |  | }, | 
| 83 |  |  |  |  |  |  | 'type' => { | 
| 84 |  |  |  |  |  |  | type => SCALAR, | 
| 85 |  |  |  |  |  |  | optional => 1, | 
| 86 |  |  |  |  |  |  | }, | 
| 87 |  |  |  |  |  |  | 'default_type' => { | 
| 88 |  |  |  |  |  |  | type => SCALAR, | 
| 89 |  |  |  |  |  |  | optional => 1, | 
| 90 |  |  |  |  |  |  | }, | 
| 91 |  |  |  |  |  |  | 'filename' => { | 
| 92 |  |  |  |  |  |  | type => SCALAR, | 
| 93 |  |  |  |  |  |  | optional => 1, | 
| 94 |  |  |  |  |  |  | }, | 
| 95 |  |  |  |  |  |  | 'filter' => { | 
| 96 |  |  |  |  |  |  | optional => 1, | 
| 97 |  |  |  |  |  |  | type => CODEREF, | 
| 98 |  |  |  |  |  |  | }, | 
| 99 |  |  |  |  |  |  | 'replace_name' => { | 
| 100 |  |  |  |  |  |  | optional => 1, | 
| 101 |  |  |  |  |  |  | type => SCALAR, | 
| 102 |  |  |  |  |  |  | }, | 
| 103 |  |  |  |  |  |  | 'cachepath' => { | 
| 104 |  |  |  |  |  |  | optional => 1, | 
| 105 |  |  |  |  |  |  | type => SCALAR, | 
| 106 |  |  |  |  |  |  | }, | 
| 107 |  |  |  |  |  |  | 'path' => { | 
| 108 |  |  |  |  |  |  | optional => 1, | 
| 109 |  |  |  |  |  |  | type => SCALAR, | 
| 110 |  |  |  |  |  |  | }, | 
| 111 |  |  |  |  |  |  | })}, $class; | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | my $path = $self->path(); | 
| 114 |  |  |  |  |  |  | my $type = $self->{type} || ''; | 
| 115 |  |  |  |  |  |  | my $name = $self->{name}; | 
| 116 |  |  |  |  |  |  | my $filename = $self->{filename}; | 
| 117 |  |  |  |  |  |  | my $alt_filename = $self->{filename}; | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | unless ($filename or ref($name) eq 'SCALAR') { | 
| 120 |  |  |  |  |  |  | $filename = $self->{filename} = "$path/$type/$name"; | 
| 121 |  |  |  |  |  |  | my $type = $self->{default_type} || ''; | 
| 122 |  |  |  |  |  |  | unless (-f "$filename.html") { | 
| 123 |  |  |  |  |  |  | $filename = $self->{filename} = "$path/$type/$name"; | 
| 124 |  |  |  |  |  |  | } | 
| 125 |  |  |  |  |  |  | unless (-f "$filename.html") { | 
| 126 |  |  |  |  |  |  | $filename = $self->{filename} = "$path/$name"; | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  | unless (-f "$filename.html") { | 
| 129 |  |  |  |  |  |  | die "File not found: $filename"; | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  | } | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | if ($filename) { | 
| 134 |  |  |  |  |  |  | my $cachefile = $self->cachefilename; | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | my @path = split('/', $cachefile); | 
| 137 |  |  |  |  |  |  | pop @path; | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | if (-r $cachefile and ( -M $cachefile < -M  $filename . '.html')) { | 
| 140 |  |  |  |  |  |  | require Storable; | 
| 141 |  |  |  |  |  |  | $self->{parsed} = Storable::retrieve($cachefile); | 
| 142 |  |  |  |  |  |  | } elsif ( -r $filename . '.html') { | 
| 143 |  |  |  |  |  |  | $self->{parsed} = $self->parse( | 
| 144 |  |  |  |  |  |  | $filename . '.html', | 
| 145 |  |  |  |  |  |  | name => $name, | 
| 146 |  |  |  |  |  |  | type => $self->{type}, | 
| 147 |  |  |  |  |  |  | default_type => $self->{default_type}, | 
| 148 |  |  |  |  |  |  | replace_name => $self->{replace_name} || 'home', | 
| 149 |  |  |  |  |  |  | path => $self->path, | 
| 150 |  |  |  |  |  |  | cachepath => $self->cachepath, | 
| 151 |  |  |  |  |  |  | ); | 
| 152 |  |  |  |  |  |  | my $path = ''; | 
| 153 |  |  |  |  |  |  | while (@path) { | 
| 154 |  |  |  |  |  |  | $path .= shift(@path) . '/'; | 
| 155 |  |  |  |  |  |  | unless ( -d $path ) { | 
| 156 |  |  |  |  |  |  | mkdir $path or die 'Bad path ' . $path .  " $cachefile @path"; | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  | require Storable; | 
| 160 |  |  |  |  |  |  | Storable::nstore($self->{parsed}, $cachefile ); | 
| 161 |  |  |  |  |  |  | } else { | 
| 162 |  |  |  |  |  |  | die "File not found: $filename"; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  | } else { | 
| 165 |  |  |  |  |  |  | unless (ref($name) eq 'SCALAR') { | 
| 166 |  |  |  |  |  |  | croak "no template for $name [$path/$type/$name.tmpl]" unless (-f "$path/$type/$name.tmpl"); | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  | $self->{parsed} = $self->parse( | 
| 169 |  |  |  |  |  |  | $name, | 
| 170 |  |  |  |  |  |  | name => '_scalar_', | 
| 171 |  |  |  |  |  |  | replace_name => $self->{replace_name} || 'home', | 
| 172 |  |  |  |  |  |  | path => $self->path, | 
| 173 |  |  |  |  |  |  | cachepath => $self->cachepath, | 
| 174 |  |  |  |  |  |  | ); | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | $self; | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | # helper functions | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | sub loop | 
| 183 |  |  |  |  |  |  | { | 
| 184 |  |  |  |  |  |  | my $self = shift; | 
| 185 |  |  |  |  |  |  | my %p = validate(@_, { | 
| 186 |  |  |  |  |  |  | headers => 0, | 
| 187 |  |  |  |  |  |  | data => 0, | 
| 188 |  |  |  |  |  |  | inclusive => 0, | 
| 189 |  |  |  |  |  |  | default => 0, | 
| 190 |  |  |  |  |  |  | }); | 
| 191 |  |  |  |  |  |  | require XHTML::Instrumented::Loop; | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | XHTML::Instrumented::Loop->new(%p); | 
| 194 |  |  |  |  |  |  | } | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | sub get_form | 
| 197 |  |  |  |  |  |  | { | 
| 198 |  |  |  |  |  |  | my $self = shift; | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | require XHTML::Instrumented::Form; | 
| 201 |  |  |  |  |  |  | XHTML::Instrumented::Form->new(@_); | 
| 202 |  |  |  |  |  |  | } | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | sub replace | 
| 205 |  |  |  |  |  |  | { | 
| 206 |  |  |  |  |  |  | my $self = shift; | 
| 207 |  |  |  |  |  |  | my %p = validate(@_, { | 
| 208 |  |  |  |  |  |  | args => 0, | 
| 209 |  |  |  |  |  |  | text => 0, | 
| 210 |  |  |  |  |  |  | src => 0, | 
| 211 |  |  |  |  |  |  | replace => 0, | 
| 212 |  |  |  |  |  |  | remove => 0, | 
| 213 |  |  |  |  |  |  | remove_tag => 0, | 
| 214 |  |  |  |  |  |  | }); | 
| 215 |  |  |  |  |  |  | require XHTML::Instrumented::Control; | 
| 216 |  |  |  |  |  |  | XHTML::Instrumented::Control->new(%p); | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | sub args | 
| 220 |  |  |  |  |  |  | { | 
| 221 |  |  |  |  |  |  | my $self = shift; | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | $self->replace(args => { @_ }); | 
| 224 |  |  |  |  |  |  | } | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | our @unused; | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | # the main function | 
| 229 |  |  |  |  |  |  | sub __filename | 
| 230 |  |  |  |  |  |  | { | 
| 231 |  |  |  |  |  |  | my $self = shift; | 
| 232 |  |  |  |  |  |  | my ($path, $type, $name); | 
| 233 |  |  |  |  |  |  | unless (-f "$path/$type/$name.tmpl") { | 
| 234 |  |  |  |  |  |  | $type = $self->{default_type} || 'default'; | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  | die "no template for $name [$path/$type/$name.tmpl]" unless (-f "$path/$type/$name.tmpl"); | 
| 237 |  |  |  |  |  |  | my $file = "$path/$type/$name.tmpl"; | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | sub parse | 
| 241 |  |  |  |  |  |  | { | 
| 242 |  |  |  |  |  |  | my $self = shift; | 
| 243 |  |  |  |  |  |  | my $data = shift; | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | @unused = (); | 
| 246 |  |  |  |  |  |  | my $parser = new XML::Parser::Expat( | 
| 247 |  |  |  |  |  |  | NoExpand => 1, | 
| 248 |  |  |  |  |  |  | ErrorContext => 1, | 
| 249 |  |  |  |  |  |  | ProtocolEncoding => 'utf-8', | 
| 250 |  |  |  |  |  |  | ); | 
| 251 |  |  |  |  |  |  | $parser->setHandlers('Start' => \&_sh, | 
| 252 |  |  |  |  |  |  | 'End'   => \&_eh, | 
| 253 |  |  |  |  |  |  | 'Char'  => \&_ch, | 
| 254 |  |  |  |  |  |  | 'Attlist'  => \&_ah, | 
| 255 |  |  |  |  |  |  | 'Entity' => \&_ah, | 
| 256 |  |  |  |  |  |  | 'Element' => \&_ah, | 
| 257 |  |  |  |  |  |  | 'Default' => \&_ex, | 
| 258 |  |  |  |  |  |  | 'Unparsed' => \&_cm, | 
| 259 |  |  |  |  |  |  | 'CdataStart' => \&_cds, | 
| 260 |  |  |  |  |  |  | 'CdataEnd' => \&_cde, | 
| 261 |  |  |  |  |  |  | ); | 
| 262 |  |  |  |  |  |  | $parser->{_OFF_} = 0; | 
| 263 |  |  |  |  |  |  | $parser->{__filter__} = $self->{filter}; | 
| 264 |  |  |  |  |  |  | $parser->{__ids__} = {}; | 
| 265 |  |  |  |  |  |  | $parser->{__idr__} = {}; | 
| 266 |  |  |  |  |  |  | $parser->{__args__} = { @_ }; | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | $self->{_parser} = $parser; | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  | my $type = $self->{type}; | 
| 271 |  |  |  |  |  |  | my $name = $self->{name}; | 
| 272 |  |  |  |  |  |  | my %hash = (@_); | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | $parser->{__data__} = {};  # FIXME this may need to be set | 
| 275 |  |  |  |  |  |  | $parser->{__top__} = XHTML::Instrumented::Entry->new( | 
| 276 |  |  |  |  |  |  | tag => '__global__', | 
| 277 |  |  |  |  |  |  | flags => {}, | 
| 278 |  |  |  |  |  |  | args => {}, | 
| 279 |  |  |  |  |  |  | ); | 
| 280 |  |  |  |  |  |  | $parser->{__context__} = [ $parser->{__top__} ]; | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | if (ref($data) eq 'SCALAR') { | 
| 283 |  |  |  |  |  |  | my $html = ${$data}; | 
| 284 |  |  |  |  |  |  | eval { | 
| 285 |  |  |  |  |  |  | $parser->parse($html); | 
| 286 |  |  |  |  |  |  | }; | 
| 287 |  |  |  |  |  |  | if ($@) { | 
| 288 |  |  |  |  |  |  | die "$@"; | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  | } else { | 
| 291 |  |  |  |  |  |  | my $filename = $data; | 
| 292 |  |  |  |  |  |  | eval { | 
| 293 |  |  |  |  |  |  | $parser->parsefile($filename); | 
| 294 |  |  |  |  |  |  | }; | 
| 295 |  |  |  |  |  |  | if ($@) { | 
| 296 |  |  |  |  |  |  | croak "$@ $filename"; | 
| 297 |  |  |  |  |  |  | } | 
| 298 |  |  |  |  |  |  | } | 
| 299 |  |  |  |  |  |  | bless({ | 
| 300 |  |  |  |  |  |  | idr => $parser->{__idr__}, | 
| 301 |  |  |  |  |  |  | data => $parser->{__top__}->{data} | 
| 302 |  |  |  |  |  |  | }, 'XHTML::Intramented::Parsed'); | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | sub _get_tag | 
| 306 |  |  |  |  |  |  | { | 
| 307 |  |  |  |  |  |  | my $tag = shift; | 
| 308 |  |  |  |  |  |  | my $start = shift; | 
| 309 |  |  |  |  |  |  | my $data = $start; | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | for my $element (@$data) { | 
| 312 |  |  |  |  |  |  | next unless ref($element); | 
| 313 |  |  |  |  |  |  |  | 
| 314 |  |  |  |  |  |  | return $element if $element->{tag} eq $tag; | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | my $data = _get_tag($tag, $element->{data}); | 
| 317 |  |  |  |  |  |  | return $data if $data; | 
| 318 |  |  |  |  |  |  | } | 
| 319 |  |  |  |  |  |  | undef; | 
| 320 |  |  |  |  |  |  | } | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | sub get_tag | 
| 323 |  |  |  |  |  |  | { | 
| 324 |  |  |  |  |  |  | my $self = shift; | 
| 325 |  |  |  |  |  |  | my $tag = shift; | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | my $data = _get_tag($tag, $self->{parsed}{data}); | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | return $data; | 
| 330 |  |  |  |  |  |  | } | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | sub instrument | 
| 333 |  |  |  |  |  |  | { | 
| 334 |  |  |  |  |  |  | my $self = shift; | 
| 335 |  |  |  |  |  |  | my %p = validate(@_, { | 
| 336 |  |  |  |  |  |  | content_tag => 1, | 
| 337 |  |  |  |  |  |  | control => { | 
| 338 |  |  |  |  |  |  | }, | 
| 339 |  |  |  |  |  |  | }); | 
| 340 |  |  |  |  |  |  | my $data = {}; | 
| 341 |  |  |  |  |  |  | my $ret; | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | $data->{data} = [ $self->{parsed}{data} ]; | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | if (my $tag = $p{content_tag}) { | 
| 346 |  |  |  |  |  |  | $data = _get_tag($tag, $self->{parsed}{data}); | 
| 347 |  |  |  |  |  |  | $data->{data} = [ @{$self->{parsed}{data}} ] unless $data; | 
| 348 |  |  |  |  |  |  | } | 
| 349 |  |  |  |  |  |  | my $hash = $p{control} || {}; | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | for my $element ( @{$data->{data}} ) { | 
| 352 |  |  |  |  |  |  | if (ref($element)) { | 
| 353 |  |  |  |  |  |  | $ret .= $element->expand( | 
| 354 |  |  |  |  |  |  | context => XHTML::Instrumented::Context->new( | 
| 355 |  |  |  |  |  |  | hash => $hash, | 
| 356 |  |  |  |  |  |  | ), | 
| 357 |  |  |  |  |  |  | ); | 
| 358 |  |  |  |  |  |  | } else { | 
| 359 |  |  |  |  |  |  | $ret .= $element; | 
| 360 |  |  |  |  |  |  | } | 
| 361 |  |  |  |  |  |  | } | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | $ret; | 
| 364 |  |  |  |  |  |  | } | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | sub head | 
| 367 |  |  |  |  |  |  | { | 
| 368 |  |  |  |  |  |  | my $self = shift; | 
| 369 |  |  |  |  |  |  | my %hash = (@_); | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | return $self->instrument( | 
| 372 |  |  |  |  |  |  | content_tag => 'head', | 
| 373 |  |  |  |  |  |  | control => { %hash }, | 
| 374 |  |  |  |  |  |  | ); | 
| 375 |  |  |  |  |  |  | } | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | sub output | 
| 378 |  |  |  |  |  |  | { | 
| 379 |  |  |  |  |  |  | my $self = shift; | 
| 380 |  |  |  |  |  |  | my %hash = (@_); | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | return $self->instrument( | 
| 383 |  |  |  |  |  |  | content_tag => 'body', | 
| 384 |  |  |  |  |  |  | control => { %hash }, | 
| 385 |  |  |  |  |  |  | ); | 
| 386 |  |  |  |  |  |  | } | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | our $level = 0; | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | use Encode; | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | sub _fixup | 
| 393 |  |  |  |  |  |  | { | 
| 394 |  |  |  |  |  |  | my @ret; | 
| 395 |  |  |  |  |  |  | for my $data (@_) { | 
| 396 |  |  |  |  |  |  | $data =~ s/&/&/g; | 
| 397 |  |  |  |  |  |  | my $x = $data; | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | push @ret, $data; | 
| 400 |  |  |  |  |  |  | } | 
| 401 |  |  |  |  |  |  | @ret; | 
| 402 |  |  |  |  |  |  | } | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | sub _ex | 
| 405 |  |  |  |  |  |  | { | 
| 406 |  |  |  |  |  |  | my $self = shift; | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | push(@{$self->{__context__}[-1]->{data}}, @_); | 
| 409 |  |  |  |  |  |  | } | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | sub _cm | 
| 412 |  |  |  |  |  |  | { | 
| 413 |  |  |  |  |  |  | die "Don't know how to handle Unparsed Data"; | 
| 414 |  |  |  |  |  |  | } | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | sub _cds | 
| 417 |  |  |  |  |  |  | { | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  | } | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | sub _cde | 
| 422 |  |  |  |  |  |  | { | 
| 423 |  |  |  |  |  |  |  | 
| 424 |  |  |  |  |  |  | } | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | sub _sh | 
| 427 |  |  |  |  |  |  | { | 
| 428 |  |  |  |  |  |  | my $self = shift; | 
| 429 |  |  |  |  |  |  | my $tag = shift; | 
| 430 |  |  |  |  |  |  | my %args = @_; | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | my $top = $self->{__context__}->[-1]; | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | if (my $code = $self->{__filter__}) { | 
| 435 |  |  |  |  |  |  | $code->( | 
| 436 |  |  |  |  |  |  | tag => $tag, | 
| 437 |  |  |  |  |  |  | args => \%args, | 
| 438 |  |  |  |  |  |  | ); | 
| 439 |  |  |  |  |  |  | } | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | for my $key (keys %args) { | 
| 442 |  |  |  |  |  |  | my %hash = %{$self->{__data__}}; | 
| 443 |  |  |  |  |  |  | if ($args{$key} =~ /\@\@([A-Za-z][A-Za-z0-9_-][^.@]*)\.?([^@]*)\@\@/) { | 
| 444 |  |  |  |  |  |  | die q(Can't do this); | 
| 445 |  |  |  |  |  |  | } | 
| 446 |  |  |  |  |  |  | $args{$key} =~ s/\@\@([A-Za-z][A-Za-z0-9_-][^.@]*)\.?([^@]*)\@\@/ | 
| 447 |  |  |  |  |  |  | my @extra = split('\.', $2); | 
| 448 |  |  |  |  |  |  | my $name = $1; | 
| 449 |  |  |  |  |  |  | my $extra = $2; | 
| 450 |  |  |  |  |  |  | my $type = $hash{$1}; | 
| 451 |  |  |  |  |  |  | if (defined $type) { | 
| 452 |  |  |  |  |  |  | $type; | 
| 453 |  |  |  |  |  |  | } else { | 
| 454 |  |  |  |  |  |  | qq(-- $1 --); | 
| 455 |  |  |  |  |  |  | } | 
| 456 |  |  |  |  |  |  | /xge; | 
| 457 |  |  |  |  |  |  | } | 
| 458 |  |  |  |  |  |  | my %local = (); | 
| 459 |  |  |  |  |  |  |  | 
| 460 |  |  |  |  |  |  | my $child = $top->child( | 
| 461 |  |  |  |  |  |  | tag => $tag, | 
| 462 |  |  |  |  |  |  | args => \%args, | 
| 463 |  |  |  |  |  |  | ); | 
| 464 |  |  |  |  |  |  | if (my $id = $child->id) { | 
| 465 |  |  |  |  |  |  | warn "Duplicate id: $id" if exists $self->{__ids__}{$id}; | 
| 466 |  |  |  |  |  |  | $self->{__ids__}{$args{id}}++; | 
| 467 |  |  |  |  |  |  | $self->{__idr__}{$id} = $child; | 
| 468 |  |  |  |  |  |  | } | 
| 469 |  |  |  |  |  |  | if (exists($self->{_inform_}) && $child->name && $child->id) { | 
| 470 |  |  |  |  |  |  | $self->{_inform_}->{_ids_}{$child->id} = $child->name; | 
| 471 |  |  |  |  |  |  | $self->{_inform_}->{_names_}{$child->name} = $child->id; | 
| 472 |  |  |  |  |  |  | } | 
| 473 |  |  |  |  |  |  | if (exists($self->{_inform_}) && $child->name) { | 
| 474 |  |  |  |  |  |  | my $form_id = $self->{_inform_id_}; | 
| 475 |  |  |  |  |  |  | if ($form_id) { | 
| 476 |  |  |  |  |  |  | $self->{_inform_ids_}{$form_id}{$child->name} = $tag; | 
| 477 |  |  |  |  |  |  | } else { | 
| 478 |  |  |  |  |  |  | warn "Fix this"; | 
| 479 |  |  |  |  |  |  | } | 
| 480 |  |  |  |  |  |  | } | 
| 481 |  |  |  |  |  |  | push(@{$self->{__context__}}, | 
| 482 |  |  |  |  |  |  | $child, | 
| 483 |  |  |  |  |  |  | ); | 
| 484 |  |  |  |  |  |  | if ($tag eq 'form') { | 
| 485 |  |  |  |  |  |  | $self->xpcroak('embeded form') if ($self->{_inform_}); | 
| 486 |  |  |  |  |  |  | $self->{_inform_} = $child; | 
| 487 |  |  |  |  |  |  | if (my $id = $args{id} || $args{name}) { | 
| 488 |  |  |  |  |  |  | $self->{_inform_id_} = $id; | 
| 489 |  |  |  |  |  |  | $self->{_inform_ids_}{$id} = {}; | 
| 490 |  |  |  |  |  |  | } | 
| 491 |  |  |  |  |  |  | } | 
| 492 |  |  |  |  |  |  | return undef; | 
| 493 |  |  |  |  |  |  | } | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | { | 
| 496 |  |  |  |  |  |  | package | 
| 497 |  |  |  |  |  |  | XML::Parser::Expat; | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | sub clone { | 
| 500 |  |  |  |  |  |  | my $self = shift; | 
| 501 |  |  |  |  |  |  | my $parser = new XML::Parser::Expat( | 
| 502 |  |  |  |  |  |  | NoExpand => $self->{'NoExpand'}, | 
| 503 |  |  |  |  |  |  | ErrorContext => $self->{'ErrorContext'}, | 
| 504 |  |  |  |  |  |  | ProtocolEncoding => $self->{'ProtocolEncoding'}, | 
| 505 |  |  |  |  |  |  | ); | 
| 506 |  |  |  |  |  |  | $parser->{__data__} = {}; | 
| 507 |  |  |  |  |  |  | $parser->{__top__} = XHTML::Instrumented::Entry->new( | 
| 508 |  |  |  |  |  |  | tag => 'div', | 
| 509 |  |  |  |  |  |  | flags => {}, | 
| 510 |  |  |  |  |  |  | args => {}, | 
| 511 |  |  |  |  |  |  | ); | 
| 512 |  |  |  |  |  |  | $parser->{__context__} = [ $parser->{__top__} ]; | 
| 513 |  |  |  |  |  |  | return $parser; | 
| 514 |  |  |  |  |  |  | } | 
| 515 |  |  |  |  |  |  | } | 
| 516 |  |  |  |  |  |  |  | 
| 517 |  |  |  |  |  |  | sub _eh | 
| 518 |  |  |  |  |  |  | { | 
| 519 |  |  |  |  |  |  | my $self = shift; | 
| 520 |  |  |  |  |  |  | my $tag = shift; | 
| 521 |  |  |  |  |  |  | my $current = pop(@{$self->{__context__}}); | 
| 522 |  |  |  |  |  |  | my $parent = $self->{__context__}->[-1]; | 
| 523 |  |  |  |  |  |  |  | 
| 524 |  |  |  |  |  |  | my $args = { $current->args }; | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | die "mismatched tags $tag " . $current->tag unless $tag eq $current->tag; | 
| 527 |  |  |  |  |  |  |  | 
| 528 |  |  |  |  |  |  | if ($args->{class} && grep(/:removetag/, split('\s+', $args->{class}))) { | 
| 529 |  |  |  |  |  |  | $parent->append(@{$current->{data} || []}); | 
| 530 |  |  |  |  |  |  | return; | 
| 531 |  |  |  |  |  |  | } | 
| 532 |  |  |  |  |  |  | if ($args->{class} && grep(/:remove/, split('\s+', $args->{class}))) { | 
| 533 |  |  |  |  |  |  | return; | 
| 534 |  |  |  |  |  |  | } | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | if ($args->{class} && (my @names = grep(/:replace/, split('\s+', $args->{class})))) { | 
| 537 |  |  |  |  |  |  | my $out; | 
| 538 |  |  |  |  |  |  | die "Only one replace per tag" if @names != 1; | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | my $gargs = $self->{__args__}; | 
| 541 |  |  |  |  |  |  | my $default = $gargs->{default_replace}; | 
| 542 |  |  |  |  |  |  | my ($name, $file) = split('\.', $names[0]); | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | $file ||= $self->{__args__}->{replace_name} || die; | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  | if ($self->{__args__}{name} ne $file) { | 
| 547 |  |  |  |  |  |  | $out = XHTML::Instrumented->new( | 
| 548 |  |  |  |  |  |  | path  => $self->{path}, | 
| 549 |  |  |  |  |  |  | cachepath => $self->{cachepath}, | 
| 550 |  |  |  |  |  |  | %{$gargs}, | 
| 551 |  |  |  |  |  |  | name => $file, | 
| 552 |  |  |  |  |  |  | ); | 
| 553 |  |  |  |  |  |  | } else { | 
| 554 |  |  |  |  |  |  | } | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | if ($out) { | 
| 557 |  |  |  |  |  |  | my $id = $args->{id}; | 
| 558 |  |  |  |  |  |  | die 'Need an id for :replace' unless defined $id; | 
| 559 |  |  |  |  |  |  | die 'Replacement not found' unless $out->{parsed}{idr}{$id}; | 
| 560 |  |  |  |  |  |  |  | 
| 561 |  |  |  |  |  |  | $current = $out->{parsed}{idr}{$id}; | 
| 562 |  |  |  |  |  |  | } | 
| 563 |  |  |  |  |  |  | } | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  | $parent->append($current); | 
| 566 |  |  |  |  |  |  |  | 
| 567 |  |  |  |  |  |  | if ($tag eq 'form') { | 
| 568 |  |  |  |  |  |  | delete $self->{_inform_}; | 
| 569 |  |  |  |  |  |  | } | 
| 570 |  |  |  |  |  |  | } | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | sub _ah | 
| 573 |  |  |  |  |  |  | { | 
| 574 |  |  |  |  |  |  | my $self = shift; | 
| 575 |  |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  | die q(We don't do these here); | 
| 577 |  |  |  |  |  |  | } | 
| 578 |  |  |  |  |  |  |  | 
| 579 |  |  |  |  |  |  | sub _ch | 
| 580 |  |  |  |  |  |  | { | 
| 581 |  |  |  |  |  |  | my $self = shift; | 
| 582 |  |  |  |  |  |  | my $context = $self->{__context__}->[-1]; | 
| 583 |  |  |  |  |  |  | my $data = shift; | 
| 584 |  |  |  |  |  |  | my %hash = %{$self->{__data__}}; | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | my @ret; | 
| 587 |  |  |  |  |  |  |  | 
| 588 |  |  |  |  |  |  | $data = join('', _fixup($data)); | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  | if ($context->{flags} & 1) { | 
| 591 |  |  |  |  |  |  | ; | 
| 592 |  |  |  |  |  |  | } else { | 
| 593 |  |  |  |  |  |  | my @x = split(/(\@\@[A-Za-z][A-Za-z0-9_-][^.@]*\.?[^@]*\@\@)/, $data); | 
| 594 |  |  |  |  |  |  | if (@x > 1) { | 
| 595 |  |  |  |  |  |  | for my $p (@x) { | 
| 596 |  |  |  |  |  |  | if ($p =~ m/\@\@([A-Za-z][A-Za-z0-9_-][^.@]*)\.?([^@]*)\@\@/) { | 
| 597 |  |  |  |  |  |  | push @ret, | 
| 598 |  |  |  |  |  |  | XHTML::Instrumented::Entry->new( | 
| 599 |  |  |  |  |  |  | tag => '__special__', | 
| 600 |  |  |  |  |  |  | flags => {rs => 1}, | 
| 601 |  |  |  |  |  |  | args => {}, | 
| 602 |  |  |  |  |  |  | data => [ "-- $p --" ], | 
| 603 |  |  |  |  |  |  | id => $1, | 
| 604 |  |  |  |  |  |  | ); | 
| 605 |  |  |  |  |  |  | } else { | 
| 606 |  |  |  |  |  |  | push @ret, $p; | 
| 607 |  |  |  |  |  |  | } | 
| 608 |  |  |  |  |  |  | } | 
| 609 |  |  |  |  |  |  | } else { | 
| 610 |  |  |  |  |  |  | push @ret, $data; | 
| 611 |  |  |  |  |  |  | } | 
| 612 |  |  |  |  |  |  | $data =~ s/\@\@([A-Za-z][A-Za-z0-9_-][^.@]*)\.?([^@]*)\@\@/ | 
| 613 |  |  |  |  |  |  | my @extra = split('\.', $2); | 
| 614 |  |  |  |  |  |  | my $name = $1; | 
| 615 |  |  |  |  |  |  | my $extra = $2; | 
| 616 |  |  |  |  |  |  | my $type = $hash{$1}; | 
| 617 |  |  |  |  |  |  | XHTML::Instrumented::Entry->new( | 
| 618 |  |  |  |  |  |  | tag => '__special__', | 
| 619 |  |  |  |  |  |  | flags => {}, | 
| 620 |  |  |  |  |  |  | args => {}, | 
| 621 |  |  |  |  |  |  | id => $name, | 
| 622 |  |  |  |  |  |  | ); | 
| 623 |  |  |  |  |  |  | /xge; | 
| 624 |  |  |  |  |  |  | } | 
| 625 |  |  |  |  |  |  | push(@{$context->{data}}, @ret); | 
| 626 |  |  |  |  |  |  | } | 
| 627 |  |  |  |  |  |  |  | 
| 628 |  |  |  |  |  |  | 1; | 
| 629 |  |  |  |  |  |  | __END__ |