/[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.16 - (show annotations) (download)
Wed Oct 17 10:46:26 2007 UTC (17 years ago) by wakaba
Branch: MAIN
Changes since 1.15: +12 -2 lines
++ whatpm/Whatpm/ChangeLog	17 Oct 2007 10:45:53 -0000
	* Makefile (clean): New rule.

	* NanoDOM.pm (public_id, system_id): New attributes.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24