/[suikacvs]/markup/html/whatpm/Whatpm/NanoDOM.pm
Suika

Contents of /markup/html/whatpm/Whatpm/NanoDOM.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.14 - (show annotations) (download)
Mon Jul 16 07:48:19 2007 UTC (17 years, 3 months ago) by wakaba
Branch: MAIN
Changes since 1.13: +5 -1 lines
++ whatpm/t/ChangeLog	16 Jul 2007 07:48:16 -0000
	* content-model-1.dat, content-model-2.dat: Add "in XML:charset"
	error for test data that has |charset| in XML context.

	* content-model-2.dat: Test data for "in XML:charset", "in XML:lang",
	and "in HTML:xml:lang" are added.

2007-07-16  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ChangeLog	16 Jul 2007 07:33:46 -0000
	* ContentChecker.pm: Report error if |xml:lang|
	in HTML, |lang| in XML, |xmlns| in XML, and |meta| |charset|
	in XML.

	* NanoDOM.pm (Attr.owner_document): New attribute.

2007-07-16  Wakaba  <wakaba@suika.fam.cx>

1 =head1 NAME
2
3 Whatpm::NanoDOM - A Non-Conforming Implementation of DOM Subset
4
5 =head1 DESCRIPTION
6
7 The C<Whatpm::NanoDOM> module contains a non-conforming implementation
8 of a subset of DOM. It is the intention that this module is
9 used only for the purpose of testing the C<Whatpm::HTML> module.
10
11 See source code if you would like to know what it does.
12
13 =cut
14
15 package Whatpm::NanoDOM;
16 use strict;
17
18 require Scalar::Util;
19
20 package Whatpm::NanoDOM::DOMImplementation;
21
22 sub create_document ($) {
23 return Whatpm::NanoDOM::Document->new;
24 } # create_document
25
26 package Whatpm::NanoDOM::Node;
27
28 sub new ($) {
29 my $class = shift;
30 my $self = bless {}, $class;
31 return $self;
32 } # new
33
34 sub parent_node ($) {
35 return shift->{parent_node};
36 } # parent_node
37
38 sub manakai_parent_element ($) {
39 my $self = shift;
40 my $parent = $self->{parent_node};
41 while (defined $parent) {
42 if ($parent->node_type == 1) {
43 return $parent;
44 } else {
45 $parent = $parent->{parent_node};
46 }
47 }
48 return undef;
49 } # manakai_parent_element
50
51 sub child_nodes ($) {
52 return shift->{child_nodes} || [];
53 } # child_nodes
54
55 ## NOTE: Only applied to Elements and Documents
56 sub append_child ($$) {
57 my ($self, $new_child) = @_;
58 if (defined $new_child->{parent_node}) {
59 my $parent_list = $new_child->{parent_node}->{child_nodes};
60 for (0..$#$parent_list) {
61 if ($parent_list->[$_] eq $new_child) {
62 splice @$parent_list, $_, 1;
63 }
64 }
65 }
66 push @{$self->{child_nodes}}, $new_child;
67 $new_child->{parent_node} = $self;
68 Scalar::Util::weaken ($new_child->{parent_node});
69 return $new_child;
70 } # append_child
71
72 ## NOTE: Only applied to Elements and Documents
73 sub insert_before ($$;$) {
74 my ($self, $new_child, $ref_child) = @_;
75 if (defined $new_child->{parent_node}) {
76 my $parent_list = $new_child->{parent_node}->{child_nodes};
77 for (0..$#$parent_list) {
78 if ($parent_list->[$_] eq $new_child) {
79 splice @$parent_list, $_, 1;
80 }
81 }
82 }
83 my $i = @{$self->{child_nodes}};
84 if (defined $ref_child) {
85 for (0..$#{$self->{child_nodes}}) {
86 if ($self->{child_nodes}->[$_] eq $ref_child) {
87 $i = $_;
88 last;
89 }
90 }
91 }
92 splice @{$self->{child_nodes}}, $i, 0, $new_child;
93 $new_child->{parent_node} = $self;
94 Scalar::Util::weaken ($new_child->{parent_node});
95 return $new_child;
96 } # insert_before
97
98 ## NOTE: Only applied to Elements and Documents
99 sub remove_child ($$) {
100 my ($self, $old_child) = @_;
101 my $parent_list = $self->{child_nodes};
102 for (0..$#$parent_list) {
103 if ($parent_list->[$_] eq $old_child) {
104 splice @$parent_list, $_, 1;
105 }
106 }
107 delete $old_child->{parent_node};
108 return $old_child;
109 } # remove_child
110
111 ## NOTE: Only applied to Elements and Documents
112 sub has_child_nodes ($) {
113 return @{shift->{child_nodes}} > 0;
114 } # has_child_nodes
115
116 ## NOTE: Only applied to Elements and Documents
117 sub first_child ($) {
118 my $self = shift;
119 return $self->{child_nodes}->[0];
120 } # first_child
121
122 ## NOTE: Only applied to Elements and Documents
123 sub last_child ($) {
124 my $self = shift;
125 return @{$self->{child_nodes}} ? $self->{child_nodes}->[-1] : undef;
126 } # last_child
127
128 ## NOTE: Only applied to Elements and Documents
129 sub previous_sibling ($) {
130 my $self = shift;
131 my $parent = $self->{parent_node};
132 return undef unless defined $parent;
133 my $r;
134 for (@{$parent->{child_nodes}}) {
135 if ($_ eq $self) {
136 return $r;
137 } else {
138 $r = $_;
139 }
140 }
141 return undef;
142 } # previous_sibling
143
144 sub prefix ($;$) {
145 my $self = shift;
146 if (@_) {
147 $self->{prefix} = shift;
148 }
149 return $self->{prefix};
150 } # prefix
151
152 sub ELEMENT_NODE () { 1 }
153 sub ATTRIBUTE_NODE () { 2 }
154 sub TEXT_NODE () { 3 }
155 sub CDATA_SECTION_NODE () { 4 }
156 sub ENTITY_REFERENCE_NODE () { 5 }
157 sub ENTITY_NODE () { 6 }
158 sub PROCESSING_INSTRUCTION_NODE () { 7 }
159 sub COMMENT_NODE () { 8 }
160 sub DOCUMENT_NODE () { 9 }
161 sub DOCUMENT_TYPE_NODE () { 10 }
162 sub DOCUMENT_FRAGMENT_NODE () { 11 }
163 sub NOTATION_NODE () { 12 }
164
165 package Whatpm::NanoDOM::Document;
166 push our @ISA, 'Whatpm::NanoDOM::Node';
167
168 sub new ($) {
169 my $self = shift->SUPER::new;
170 $self->{child_nodes} = [];
171 return $self;
172 } # new
173
174 ## A manakai extension
175 sub manakai_append_text ($$) {
176 my $self = shift;
177 if (@{$self->{child_nodes}} and
178 $self->{child_nodes}->[-1]->node_type == 3) {
179 $self->{child_nodes}->[-1]->manakai_append_text (shift);
180 } else {
181 my $text = $self->create_text_node (shift);
182 $self->append_child ($text);
183 }
184 } # manakai_append_text
185
186 sub node_type () { 9 }
187
188 sub strict_error_checking {
189 return 0;
190 } # strict_error_checking
191
192 sub create_text_node ($$) {
193 shift;
194 return Whatpm::NanoDOM::Text->new (shift);
195 } # create_text_node
196
197 sub create_comment ($$) {
198 shift;
199 return Whatpm::NanoDOM::Comment->new (shift);
200 } # create_comment
201
202 ## The second parameter only supports manakai extended way
203 ## to specify qualified name - "[$prefix, $local_name]"
204 sub create_element_ns ($$$) {
205 my ($self, $nsuri, $qn) = @_;
206 return Whatpm::NanoDOM::Element->new ($self, $nsuri, $qn->[0], $qn->[1]);
207 } # create_element_ns
208
209 ## A manakai extension
210 sub create_document_type_definition ($$) {
211 shift;
212 return Whatpm::NanoDOM::DocumentType->new (shift);
213 } # create_document_type_definition
214
215 sub implementation ($) {
216 return 'Whatpm::NanoDOM::DOMImplementation';
217 } # implementation
218
219 sub document_element ($) {
220 my $self = shift;
221 for (@{$self->child_nodes}) {
222 if ($_->node_type == 1) {
223 return $_;
224 }
225 }
226 return undef;
227 } # document_element
228
229 sub adopt_node ($$) {
230 my @node = ($_[1]);
231 while (@node) {
232 my $node = shift @node;
233 $node->{owner_document} = $_[0];
234 Scalar::Util::weaken ($node->{owner_document});
235 push @node, @{$node->child_nodes};
236 push @node, @{$node->attributes or []} if $node->can ('attributes');
237 }
238 return $_[1];
239 } # adopt_node
240
241 sub manakai_is_html ($;$) {
242 if (@_ > 1) {
243 if ($_[1]) {
244 $_[0]->{manakai_is_html} = 1;
245 } else {
246 delete $_[0]->{manakai_is_html};
247 delete $_[0]->{manakai_compat_mode};
248 }
249 }
250 return $_[0]->{manakai_is_html};
251 } # manakai_is_html
252
253 sub compat_mode ($) {
254 if ($_[0]->{manakai_is_html}) {
255 if ($_[0]->{manakai_compat_mode} eq 'quirks') {
256 return 'BackCompat';
257 }
258 }
259 return 'CSS1Compat';
260 } # compat_mode
261
262 sub manakai_compat_mode ($;$) {
263 if ($_[0]->{manakai_is_html}) {
264 if (@_ > 1 and defined $_[1] and
265 {'no quirks' => 1, 'limited quirks' => 1, 'quirks' => 1}->{$_[1]}) {
266 $_[0]->{manakai_compat_mode} = $_[1];
267 }
268 return $_[0]->{manakai_compat_mode} || 'no quirks';
269 } else {
270 return 'no quirks';
271 }
272 } # manakai_compat_mode
273
274 package Whatpm::NanoDOM::Element;
275 push our @ISA, 'Whatpm::NanoDOM::Node';
276
277 sub new ($$$$$) {
278 my $self = shift->SUPER::new;
279 $self->{owner_document} = shift;
280 Scalar::Util::weaken ($self->{owner_document});
281 $self->{namespace_uri} = shift;
282 $self->{prefix} = shift;
283 $self->{local_name} = shift;
284 $self->{attributes} = {};
285 $self->{child_nodes} = [];
286 return $self;
287 } # new
288
289 sub owner_document ($) {
290 return shift->{owner_document};
291 } # owner_document
292
293 sub clone_node ($$) {
294 my ($self, $deep) = @_; ## NOTE: Deep cloning is not supported
295 my $clone = bless {
296 namespace_uri => $self->{namespace_uri},
297 prefix => $self->{prefix},
298 local_name => $self->{local_name},
299 child_nodes => [],
300 }, ref $self;
301 for my $ns (keys %{$self->{attributes}}) {
302 for my $ln (keys %{$self->{attributes}->{$ns}}) {
303 my $attr = $self->{attributes}->{$ns}->{$ln};
304 $clone->{attributes}->{$ns}->{$ln} = bless {
305 namespace_uri => $attr->{namespace_uri},
306 prefix => $attr->{prefix},
307 local_name => $attr->{local_name},
308 value => $attr->{value},
309 }, ref $self->{attributes}->{$ns}->{$ln};
310 }
311 }
312 return $clone;
313 } # clone
314
315 ## A manakai extension
316 sub manakai_append_text ($$) {
317 my $self = shift;
318 if (@{$self->{child_nodes}} and
319 $self->{child_nodes}->[-1]->node_type == 3) {
320 $self->{child_nodes}->[-1]->manakai_append_text (shift);
321 } else {
322 my $text = Whatpm::NanoDOM::Text->new (shift);
323 $self->append_child ($text);
324 }
325 } # manakai_append_text
326
327 sub text_content ($) {
328 my $self = shift;
329 my $r = '';
330 for my $child (@{$self->child_nodes}) {
331 if ($child->can ('data')) {
332 $r .= $child->data;
333 } else {
334 $r .= $child->text_content;
335 }
336 }
337 return $r;
338 } # text_content
339
340 sub attributes ($) {
341 my $self = shift;
342 my $r = [];
343 ## Order MUST be stable
344 for my $ns (sort {$a cmp $b} keys %{$self->{attributes}}) {
345 for my $ln (sort {$a cmp $b} keys %{$self->{attributes}->{$ns}}) {
346 push @$r, $self->{attributes}->{$ns}->{$ln}
347 if defined $self->{attributes}->{$ns}->{$ln};
348 }
349 }
350 return $r;
351 } # attributes
352
353 sub local_name ($) { # TODO: HTML5 case
354 return shift->{local_name};
355 } # local_name
356
357 sub manakai_local_name ($) {
358 return shift->{local_name}; # no case fixing for HTML5
359 } # manakai_local_name
360
361 sub namespace_uri ($) {
362 return shift->{namespace_uri};
363 } # namespace_uri
364
365 sub manakai_element_type_match ($$$) {
366 my ($self, $nsuri, $ln) = @_;
367 if (defined $nsuri) {
368 if (defined $self->{namespace_uri} and $nsuri eq $self->{namespace_uri}) {
369 return ($ln eq $self->{local_name});
370 } else {
371 return 0;
372 }
373 } else {
374 if (not defined $self->{namespace_uri}) {
375 return ($ln eq $self->{local_name});
376 } else {
377 return 0;
378 }
379 }
380 } # manakai_element_type_match
381
382 sub node_type { 1 }
383
384 ## TODO: HTML5 capitalization
385 sub tag_name ($) {
386 my $self = shift;
387 if (defined $self->{prefix}) {
388 return $self->{prefix} . ':' . $self->{local_name};
389 } else {
390 return $self->{local_name};
391 }
392 } # tag_name
393
394 sub get_attribute_ns ($$$) {
395 my ($self, $nsuri, $ln) = @_;
396 $nsuri = '' unless defined $nsuri;
397 return defined $self->{attributes}->{$nsuri}->{$ln}
398 ? $self->{attributes}->{$nsuri}->{$ln}->value : undef;
399 } # get_attribute_ns
400
401 sub get_attribute_node_ns ($$$) {
402 my ($self, $nsuri, $ln) = @_;
403 $nsuri = '' unless defined $nsuri;
404 return $self->{attributes}->{$nsuri}->{$ln};
405 } # get_attribute_node_ns
406
407 sub has_attribute_ns ($$$) {
408 my ($self, $nsuri, $ln) = @_;
409 $nsuri = '' unless defined $nsuri;
410 return defined $self->{attributes}->{$nsuri}->{$ln};
411 } # has_attribute_ns
412
413 ## The second parameter only supports manakai extended way
414 ## to specify qualified name - "[$prefix, $local_name]"
415 sub set_attribute_ns ($$$$) {
416 my ($self, $nsuri, $qn, $value) = @_;
417 $self->{attributes}->{$nsuri}->{$qn->[1]}
418 = Whatpm::NanoDOM::Attr->new ($self, $nsuri, $qn->[0], $qn->[1], $value);
419 } # set_attribute_ns
420
421 package Whatpm::NanoDOM::Attr;
422 push our @ISA, 'Whatpm::NanoDOM::Node';
423
424 sub new ($$$$$$) {
425 my $self = shift->SUPER::new;
426 $self->{owner_element} = shift;
427 Scalar::Util::weaken ($self->{owner_element});
428 $self->{namespace_uri} = shift;
429 $self->{prefix} = shift;
430 $self->{local_name} = shift;
431 $self->{value} = shift;
432 return $self;
433 } # new
434
435 sub namespace_uri ($) {
436 return shift->{namespace_uri};
437 } # namespace_uri
438
439 sub manakai_local_name ($) {
440 return shift->{local_name};
441 } # manakai_local_name
442
443 sub node_type { 2 }
444
445 sub owner_document ($) {
446 return shift->owner_element->owner_document;
447 } # owner_document
448
449 ## TODO: HTML5 case stuff?
450 sub name ($) {
451 my $self = shift;
452 if (defined $self->{prefix}) {
453 return $self->{prefix} . ':' . $self->{local_name};
454 } else {
455 return $self->{local_name};
456 }
457 } # name
458
459 sub value ($) {
460 return shift->{value};
461 } # value
462
463 sub owner_element ($) {
464 return shift->{owner_element};
465 } # owner_element
466
467 package Whatpm::NanoDOM::CharacterData;
468 push our @ISA, 'Whatpm::NanoDOM::Node';
469
470 sub new ($$) {
471 my $self = shift->SUPER::new;
472 $self->{data} = shift;
473 return $self;
474 } # new
475
476 ## A manakai extension
477 sub manakai_append_text ($$) {
478 my ($self, $s) = @_;
479 $self->{data} .= $s;
480 } # manakai_append_text
481
482 sub data ($) {
483 return shift->{data};
484 } # data
485
486 package Whatpm::NanoDOM::Text;
487 push our @ISA, 'Whatpm::NanoDOM::CharacterData';
488
489 sub node_type () { 3 }
490
491 package Whatpm::NanoDOM::Comment;
492 push our @ISA, 'Whatpm::NanoDOM::CharacterData';
493
494 sub node_type () { 8 }
495
496 package Whatpm::NanoDOM::DocumentType;
497 push our @ISA, 'Whatpm::NanoDOM::Node';
498
499 sub new ($$) {
500 my $self = shift->SUPER::new;
501 $self->{name} = shift;
502 return $self;
503 } # new
504
505 sub node_type () { 10 }
506
507 sub name ($) {
508 return shift->{name};
509 } # name
510
511 =head1 SEE ALSO
512
513 L<Whatpm::HTML>
514
515 =head1 AUTHOR
516
517 Wakaba <w@suika.fam.cx>.
518
519 =head1 LICENSE
520
521 Copyright 2007 Wakaba <w@suika.fam.cx>
522
523 This library is free software; you can redistribute it
524 and/or modify it under the same terms as Perl itself.
525
526 =cut
527
528 1;
529 # $Date: 2007/06/23 13:05:16 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24