| blib/lib/jQuery.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 7 | 9 | 77.7 |
| branch | n/a | ||
| condition | n/a | ||
| subroutine | 3 | 3 | 100.0 |
| pod | n/a | ||
| total | 10 | 12 | 83.3 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package jQuery; | ||||||
| 2 | |||||||
| 3 | 25 | 25 | 58716 | use warnings; | |||
| 25 | 42 | ||||||
| 25 | 821 | ||||||
| 4 | 25 | 25 | 129 | use strict; | |||
| 25 | 36 | ||||||
| 25 | 919 | ||||||
| 5 | 25 | 25 | 49278 | use XML::LibXML 1.70; | |||
| 0 | |||||||
| 0 | |||||||
| 6 | use HTML::Entities; | ||||||
| 7 | use Encode; | ||||||
| 8 | use Carp; | ||||||
| 9 | |||||||
| 10 | use base qw/jQuery::Functions Exporter/; | ||||||
| 11 | our @EXPORT = qw(jquery jQuery this); | ||||||
| 12 | |||||||
| 13 | use vars qw/$DOC $PARSER $VERSION/; | ||||||
| 14 | $VERSION = "0.004"; | ||||||
| 15 | |||||||
| 16 | my $obj_class = 'jQuery::Obj'; | ||||||
| 17 | sub this { 'jQuery::Functions'->this(@_) } | ||||||
| 18 | |||||||
| 19 | sub new { | ||||||
| 20 | my $class = shift; | ||||||
| 21 | my $string = shift; | ||||||
| 22 | my $parser; | ||||||
| 23 | my $doc; | ||||||
| 24 | if (!$PARSER){ | ||||||
| 25 | $parser = XML::LibXML->new(); | ||||||
| 26 | $parser->recover(1); | ||||||
| 27 | $parser->recover_silently(1); | ||||||
| 28 | $parser->keep_blanks(1); | ||||||
| 29 | $parser->expand_entities(1); | ||||||
| 30 | $parser->no_network(1); | ||||||
| 31 | local $XML::LibXML::skipXMLDeclaration = 1; | ||||||
| 32 | local $XML::LibXML::skipDTD = 1; | ||||||
| 33 | local $XML::LibXML::setTagCompression = 1; | ||||||
| 34 | $PARSER = $parser; | ||||||
| 35 | } else { | ||||||
| 36 | $parser = $PARSER; | ||||||
| 37 | } | ||||||
| 38 | |||||||
| 39 | if ($string){ | ||||||
| 40 | if ($string =~ /^http/){ | ||||||
| 41 | $string = $class->get($string); | ||||||
| 42 | } elsif ($string =~ /^<\?xml/) { | ||||||
| 43 | $doc = $parser->parse_string($string); | ||||||
| 44 | } else { | ||||||
| 45 | $doc = $parser->parse_html_string($string); | ||||||
| 46 | } | ||||||
| 47 | $DOC = $doc; | ||||||
| 48 | } | ||||||
| 49 | |||||||
| 50 | return bless({ | ||||||
| 51 | document => $doc | ||||||
| 52 | }, __PACKAGE__); | ||||||
| 53 | } | ||||||
| 54 | |||||||
| 55 | *jQuery = \&jquery; | ||||||
| 56 | sub jquery { | ||||||
| 57 | my ($selector, $context) = (shift,shift); | ||||||
| 58 | my $this; | ||||||
| 59 | my $c; | ||||||
| 60 | |||||||
| 61 | if ( ref ($selector) eq __PACKAGE__ ){ | ||||||
| 62 | $this->{document} = $selector->{document}; | ||||||
| 63 | $selector = $context; | ||||||
| 64 | $context = shift; | ||||||
| 65 | } else { | ||||||
| 66 | $this->{document} = $DOC; | ||||||
| 67 | } | ||||||
| 68 | |||||||
| 69 | if ($context){ | ||||||
| 70 | if (@_){ | ||||||
| 71 | $context = bless([$context,@_], ref $context); | ||||||
| 72 | } | ||||||
| 73 | return jQuery($context)->find($selector); | ||||||
| 74 | } | ||||||
| 75 | |||||||
| 76 | if ( ref($selector) eq $obj_class ){ | ||||||
| 77 | return $selector; | ||||||
| 78 | } | ||||||
| 79 | |||||||
| 80 | bless($this, $obj_class); | ||||||
| 81 | $this->{nodes} = []; | ||||||
| 82 | return $this if !$selector; | ||||||
| 83 | |||||||
| 84 | my $nodes; | ||||||
| 85 | if ( ref($selector) =~ /XML::/i ){ | ||||||
| 86 | if ($selector->isa('ARRAY')){ | ||||||
| 87 | $nodes = $selector; | ||||||
| 88 | } else { | ||||||
| 89 | $nodes = [$selector]; | ||||||
| 90 | } | ||||||
| 91 | } elsif ( ref ($selector) eq 'ARRAY' ){ | ||||||
| 92 | $nodes = $selector; | ||||||
| 93 | } elsif ($this->is_HTML($selector)) { | ||||||
| 94 | $nodes = $this->createNode($selector); | ||||||
| 95 | } else { | ||||||
| 96 | $nodes = $this->_find($selector); | ||||||
| 97 | ##another try to create html fragment | ||||||
| 98 | if (!$nodes->[0]){ | ||||||
| 99 | $nodes = $this->createNode($selector); | ||||||
| 100 | } | ||||||
| 101 | } | ||||||
| 102 | |||||||
| 103 | return $this->pushStack(@$nodes); | ||||||
| 104 | } | ||||||
| 105 | |||||||
| 106 | ##something like pushStack in jquery | ||||||
| 107 | sub pushStack { | ||||||
| 108 | my $self = shift; | ||||||
| 109 | my @elements; | ||||||
| 110 | |||||||
| 111 | return jQuery($self,@_) unless $self->isa('HASH') and | ||||||
| 112 | ref $self eq $obj_class; | ||||||
| 113 | @elements = ref $_[0] eq 'ARRAY' | ||||||
| 114 | ? @{$_[0]} | ||||||
| 115 | : @_; | ||||||
| 116 | |||||||
| 117 | #save old object | ||||||
| 118 | $self->{prevObject} = $self->{nodes}; | ||||||
| 119 | |||||||
| 120 | #$self = bless ([@elements], $obj_class); | ||||||
| 121 | $self->{nodes} = \@elements; | ||||||
| 122 | return $self; | ||||||
| 123 | } | ||||||
| 124 | |||||||
| 125 | sub toArray { | ||||||
| 126 | my $self = shift; | ||||||
| 127 | my @nodes; | ||||||
| 128 | if ($self->isa('ARRAY')) { | ||||||
| 129 | @nodes = @{$self}; | ||||||
| 130 | } elsif ($self->isa('HASH')) { | ||||||
| 131 | @nodes = @{$self->{nodes}}; | ||||||
| 132 | } else { | ||||||
| 133 | @nodes = ($self); | ||||||
| 134 | } | ||||||
| 135 | |||||||
| 136 | return wantarray ? | ||||||
| 137 | @nodes | ||||||
| 138 | : \@nodes; | ||||||
| 139 | } | ||||||
| 140 | |||||||
| 141 | sub getNodes { | ||||||
| 142 | my $self = shift; | ||||||
| 143 | my @nodes; | ||||||
| 144 | if ($self->isa('ARRAY')) { | ||||||
| 145 | @nodes = @{$self}; | ||||||
| 146 | } elsif ($self->isa('HASH')) { | ||||||
| 147 | @nodes = @{$self->{nodes}}; | ||||||
| 148 | } else { | ||||||
| 149 | @nodes = ($self); | ||||||
| 150 | } | ||||||
| 151 | return wantarray ? @nodes : bless(\@nodes, $obj_class); | ||||||
| 152 | } | ||||||
| 153 | |||||||
| 154 | |||||||
| 155 | sub _find { | ||||||
| 156 | my ($this, $selector, $context ) = @_; | ||||||
| 157 | $context ||= $this->document; | ||||||
| 158 | my @nodes; | ||||||
| 159 | eval { | ||||||
| 160 | if ($selector !~ m/\//){ | ||||||
| 161 | $selector = $this->_translate_css_to_xpath($selector,$context->nodePath); | ||||||
| 162 | } | ||||||
| 163 | @nodes = $context->findnodes($selector) if $selector; | ||||||
| 164 | }; | ||||||
| 165 | |||||||
| 166 | if ($@){ | ||||||
| 167 | croak $@; | ||||||
| 168 | } | ||||||
| 169 | return wantarray | ||||||
| 170 | ? @nodes | ||||||
| 171 | : \@nodes; | ||||||
| 172 | } | ||||||
| 173 | |||||||
| 174 | #Should I use HTML::Selector::XPath and port some jquery selectors by hand ?? | ||||||
| 175 | #it's well maintained | ||||||
| 176 | #this is faster but not easy to maintain and not quiet mature | ||||||
| 177 | sub _translate_css_to_xpath { | ||||||
| 178 | my $self = shift; | ||||||
| 179 | my $start_query = shift; | ||||||
| 180 | my $old_query = shift; | ||||||
| 181 | my $custompath = shift || ''; | ||||||
| 182 | my @args; | ||||||
| 183 | $start_query =~ s/\((.*?),(.*?)\)/\($1 |
||||||
| 184 | my @queries = split(/\,/,$start_query); | ||||||
| 185 | my $this_query = 0; | ||||||
| 186 | |||||||
| 187 | foreach my $query (@queries){ | ||||||
| 188 | $query =~ s/ |
||||||
| 189 | #remove all leading and ending whitespaces | ||||||
| 190 | $query =~ s/^\s+|\s+$//g; | ||||||
| 191 | $query =~ s/\s+/ | ||||||
| 192 | ##add one whitespace at the beginning | ||||||
| 193 | $query = " ".$query; | ||||||
| 194 | |||||||
| 195 | my $selector; | ||||||
| 196 | $selector = $old_query if $old_query; | ||||||
| 197 | |||||||
| 198 | my $pos = 0; | ||||||
| 199 | my $directpath = 0; | ||||||
| 200 | my $empty_path =0; | ||||||
| 201 | my $single = 0; | ||||||
| 202 | |||||||
| 203 | ##setting starting search path custom path is // which search down all elements | ||||||
| 204 | my $path = '//'; | ||||||
| 205 | $path = $custompath if $custompath; | ||||||
| 206 | |||||||
| 207 | ##I wrote this some while ago, I feel dizzy when I try to read it again | ||||||
| 208 | while ($query =~ /([\s|\.|\:|\#]?)((\[.*?\])|(~)|(\+)|(\>)|(\<)|(\*)|([\w\-]+(\(.*?\))?))/g){ | ||||||
| 209 | |||||||
| 210 | my $type = $1; | ||||||
| 211 | my $value = $2; | ||||||
| 212 | my $pos2 = 0; | ||||||
| 213 | |||||||
| 214 | ##set empty starting path | ||||||
| 215 | if ($custompath eq 'empty' && $pos2 eq '0'){ | ||||||
| 216 | $path = ''; | ||||||
| 217 | ###if we want to search direct childrens | ||||||
| 218 | } elsif ($directpath || $empty_path){ | ||||||
| 219 | $path = '/'; | ||||||
| 220 | $path = '//' if $directpath eq '2'; | ||||||
| 221 | $path = '' if $empty_path; | ||||||
| 222 | if (($type =~ /(\:|\.|\#)/ || $value =~ /\[.*?\]/)){ | ||||||
| 223 | $selector .= $path.'*'; | ||||||
| 224 | } | ||||||
| 225 | |||||||
| 226 | #reset direct path | ||||||
| 227 | $directpath = 0; | ||||||
| 228 | ##reset empty path | ||||||
| 229 | $empty_path = 0; | ||||||
| 230 | } else { | ||||||
| 231 | $path = '//'; | ||||||
| 232 | } | ||||||
| 233 | |||||||
| 234 | $type =~ s/\s+//; | ||||||
| 235 | $value =~ s/\s+//; | ||||||
| 236 | if ($pos == 0){ | ||||||
| 237 | $path = $custompath if $custompath && $custompath ne 'empty'; | ||||||
| 238 | if (($type =~ /(\:|\.|\#)/ || $value =~ /\[.*?\]/)){ | ||||||
| 239 | $selector .= $path.'*'; | ||||||
| 240 | } | ||||||
| 241 | } | ||||||
| 242 | |||||||
| 243 | if ($value eq '*'){ | ||||||
| 244 | $selector .= $path.'*'; | ||||||
| 245 | } elsif ($value eq '>'){ | ||||||
| 246 | $directpath = 1; | ||||||
| 247 | } elsif ($value eq '<'){ | ||||||
| 248 | $directpath = 2; | ||||||
| 249 | } elsif ($value eq '+'){ | ||||||
| 250 | $selector .= '/following-sibling::'; | ||||||
| 251 | $empty_path = 1; | ||||||
| 252 | $single = 1; | ||||||
| 253 | } elsif ($value eq '~'){ | ||||||
| 254 | $selector .= '/following-sibling::'; | ||||||
| 255 | $empty_path = 1; | ||||||
| 256 | } elsif ($value =~ /\[(.*?)\]/){ | ||||||
| 257 | |||||||
| 258 | my ($name, $value) = split(/\s*=\s*/, $1, 2); | ||||||
| 259 | if (defined $value) { | ||||||
| 260 | for ($value) { | ||||||
| 261 | s/^['"]//; | ||||||
| 262 | s/['"]$//; | ||||||
| 263 | } | ||||||
| 264 | |||||||
| 265 | if ($name =~ s/\^$//){ | ||||||
| 266 | $selector .= "[starts-with (\@$name,'$value')]"; | ||||||
| 267 | } elsif ($name =~ s/\$$//){ | ||||||
| 268 | #$selector .= "[ends-with (\@$name,'$value')]"; | ||||||
| 269 | $selector .= "[substring(\@$name, string-length(\@$name) - string-length('$value')+ 1, string-length(\@$name))= '$value']"; | ||||||
| 270 | } elsif ($name =~ s/\*$//){ | ||||||
| 271 | $selector .= "[contains (\@$name,'$value')]"; | ||||||
| 272 | } elsif ($name =~ s/\|$//){ | ||||||
| 273 | $selector .= "[\@$name ='$value'"." or "."starts-with(\@$name,'$value".'-'."')]"; | ||||||
| 274 | } else { | ||||||
| 275 | $selector .= "[\@$name='$value']"; | ||||||
| 276 | } | ||||||
| 277 | |||||||
| 278 | } else { | ||||||
| 279 | $selector .= "[\@$name]"; | ||||||
| 280 | } | ||||||
| 281 | |||||||
| 282 | ##this is tag | ||||||
| 283 | } elsif (!$type){ | ||||||
| 284 | $selector .= $path.$value; | ||||||
| 285 | ##this is class | ||||||
| 286 | } elsif ($type eq "."){ | ||||||
| 287 | ##finally found a good solution from | ||||||
| 288 | ##http://plasmasturm.org/log/444/ | ||||||
| 289 | $selector .= '[ contains(concat( " ", @class, " " ),concat( " ", "'.$value.'", " " )) ]'; | ||||||
| 290 | |||||||
| 291 | ##id selector | ||||||
| 292 | } elsif ($type eq '#'){ | ||||||
| 293 | $selector .= '[@id="'.$value.'"]'; | ||||||
| 294 | } | ||||||
| 295 | |||||||
| 296 | ###pseduo-class | ||||||
| 297 | elsif ($type eq ':'){ | ||||||
| 298 | |||||||
| 299 | if ($value eq "first-child"){ | ||||||
| 300 | $selector .= '[1]'; | ||||||
| 301 | } elsif ($value eq "first"){ | ||||||
| 302 | #$selector = "getIndex($selector,'eq','0')"; | ||||||
| 303 | $selector .= '[position() = 1]'; ##this doesn't really do the job exactly as jQuery | ||||||
| 304 | } elsif ($value eq "odd"){ | ||||||
| 305 | #$selector = 'getOdd('.$selector.')'; | ||||||
| 306 | $selector .= '[position() mod 2 != 1]'; ##this doesn't do the job exactly as jQuery | ||||||
| 307 | } elsif ($value eq "even"){ | ||||||
| 308 | #$selector = 'getEven('.$selector.')'; | ||||||
| 309 | $selector .= '[position() mod 2 != 0]'; ##this doesn't do the job exactly as jQuery | ||||||
| 310 | } elsif ($value =~ /(gt|lt|eq)\((.*?)\)/){ | ||||||
| 311 | $selector = "getIndex($selector,'$1','$2')"; | ||||||
| 312 | } elsif ($value =~ /nth-child\((.*?)\)/){ | ||||||
| 313 | $selector .= "[position() = $1]"; | ||||||
| 314 | } elsif ($value =~ /has\((.*?)\)/){ | ||||||
| 315 | $selector = "getHas($selector,'$1')"; | ||||||
| 316 | } elsif ($value =~ /not\((.*?)\)/){ | ||||||
| 317 | $selector = "getNot($selector,'$1')"; | ||||||
| 318 | } elsif ($value eq "button"){ | ||||||
| 319 | #$selector .= '[@type="button"]'; | ||||||
| 320 | $selector = "getButton($selector)"; | ||||||
| 321 | } elsif ($value =~ /(checkbox|file|hidden|image|text|submit|radio|password|reset)/){ | ||||||
| 322 | $selector .= "[\@type='$value']"; | ||||||
| 323 | } elsif ($value eq "checked"){ | ||||||
| 324 | $selector .= '[@checked="checked"]'; | ||||||
| 325 | } elsif ($value eq "selected"){ | ||||||
| 326 | $selector .= '[@selected="selected"]'; | ||||||
| 327 | } elsif ($value eq "disabled"){ | ||||||
| 328 | $selector .= '[@disabled]'; | ||||||
| 329 | } elsif ($value eq "enabled"){ | ||||||
| 330 | $selector .= '[not(@disabled)]'; | ||||||
| 331 | } elsif ($value =~ /contains\((.*?)\)/){ | ||||||
| 332 | my $str = $1; | ||||||
| 333 | for ($str) { | ||||||
| 334 | s/^['"]//; | ||||||
| 335 | s/['"]$//; | ||||||
| 336 | } | ||||||
| 337 | $selector .= "[contains(.,'$str')]"; | ||||||
| 338 | } elsif ($value eq "empty"){ | ||||||
| 339 | $selector .= '[not(node())]'; | ||||||
| 340 | } elsif ($value eq "only-child"){ | ||||||
| 341 | |||||||
| 342 | my ($str1, $str2) = $selector =~ /(.*)\/(.*)/; | ||||||
| 343 | if ($str1 =~ s/\/$//){ | ||||||
| 344 | $str2 = '/'.$str2; | ||||||
| 345 | $selector = $str1.'//child::*/parent::*[count(*)=1]'.'/'.$str2; | ||||||
| 346 | } else{ | ||||||
| 347 | $selector = $str1.'/child::*/parent::*[count(*)=1]'.'/'.$str2; | ||||||
| 348 | } | ||||||
| 349 | |||||||
| 350 | } elsif ($value eq "header"){ | ||||||
| 351 | $selector = "getHeaders($selector)"; | ||||||
| 352 | } elsif ($value eq "parent"){ | ||||||
| 353 | $selector .= '[(node())]'; | ||||||
| 354 | } elsif ($value eq "last"){ | ||||||
| 355 | #$selector = "getLast($selector)"; | ||||||
| 356 | $selector .= '[position()=last()]'; | ||||||
| 357 | } elsif ($value eq "last-child"){ | ||||||
| 358 | $selector .= "[last()]"; | ||||||
| 359 | } | ||||||
| 360 | } | ||||||
| 361 | $pos++; $pos2++; | ||||||
| 362 | } | ||||||
| 363 | if ($single){ | ||||||
| 364 | $selector .= '[1]'; | ||||||
| 365 | $single = 0; | ||||||
| 366 | } | ||||||
| 367 | $this_query++; | ||||||
| 368 | push (@args,$selector); | ||||||
| 369 | } | ||||||
| 370 | return join(' | ',@args); | ||||||
| 371 | } | ||||||
| 372 | |||||||
| 373 | sub as_HTML { | ||||||
| 374 | my $self = shift; | ||||||
| 375 | my $doc = $self->document; | ||||||
| 376 | if (ref($doc) eq 'XML::LibXML::Document' ){ | ||||||
| 377 | my $html = $doc->serialize_html(); | ||||||
| 378 | if ($html =~ m/ /g){ |
||||||
| 379 | $html =~ s/(?:.*?) (.*)<\/div>(?:.*)/$1/s; |
||||||
| 380 | } | ||||||
| 381 | return $html; | ||||||
| 382 | } | ||||||
| 383 | return $doc->getDocumentElement->html(); | ||||||
| 384 | } | ||||||
| 385 | |||||||
| 386 | sub as_XML { | ||||||
| 387 | my $doc = $_[0]->document; | ||||||
| 388 | if ($$DOC ne $$doc){ | ||||||
| 389 | if (ref($doc) eq 'XML::LibXML::Document' ){ | ||||||
| 390 | my $xml = $doc->serialize(); | ||||||
| 391 | if ($xml =~ m/ /g){ |
||||||
| 392 | $xml =~ s/(?:.*?) (.*)<\/div>(?:.*)/$1/s; |
||||||
| 393 | } | ||||||
| 394 | return $xml; | ||||||
| 395 | } | ||||||
| 396 | return $doc->getDocumentElement->html(); | ||||||
| 397 | } | ||||||
| 398 | |||||||
| 399 | return $doc->serialize(); | ||||||
| 400 | } | ||||||
| 401 | |||||||
| 402 | |||||||
| 403 | sub is_HTML { | ||||||
| 404 | my ($self,$html) = @_; | ||||||
| 405 | ### very permative solution but it seems | ||||||
| 406 | ### to work with all tests so far | ||||||
| 407 | if ($html =~ /<.*>/g){ | ||||||
| 408 | return 1; | ||||||
| 409 | } | ||||||
| 410 | return 0; | ||||||
| 411 | } | ||||||
| 412 | |||||||
| 413 | sub createNode { | ||||||
| 414 | my ($self,$html) = @_; | ||||||
| 415 | my $node; | ||||||
| 416 | if (!$PARSER){ $self->new(); } | ||||||
| 417 | $html = " ".$html." "; |
||||||
| 418 | if ($html =~ /^ <\?xml/) { |
||||||
| 419 | $node = $PARSER->parse_string($html); | ||||||
| 420 | } else{ | ||||||
| 421 | $node = $PARSER->parse_html_string($html); | ||||||
| 422 | } | ||||||
| 423 | |||||||
| 424 | $DOC = $node if !$DOC; | ||||||
| 425 | $node->removeInternalSubset; | ||||||
| 426 | my $nodes = $node->getDocumentElement->findnodes("//*[\@class='REMOVE_THIS_ELEMENT']")->[0]->childNodes; | ||||||
| 427 | return $nodes; | ||||||
| 428 | } | ||||||
| 429 | |||||||
| 430 | sub createNode2 { | ||||||
| 431 | my ($self,$html) = @_; | ||||||
| 432 | $html = " ".$html." "; |
||||||
| 433 | my $new = $self->new($html); | ||||||
| 434 | return $new->jQuery('.REMOVE_THIS_ELEMENT *'); | ||||||
| 435 | } | ||||||
| 436 | |||||||
| 437 | ##detect node parent document | ||||||
| 438 | sub document { | ||||||
| 439 | my $self = shift; | ||||||
| 440 | my $doc; | ||||||
| 441 | if ($self->isa('ARRAY') && $self->[0]){ | ||||||
| 442 | $doc = $self->[0]->ownerDocument; | ||||||
| 443 | } elsif ($self->isa('HASH') && $self->{document}){ | ||||||
| 444 | $doc = $self->{document}; | ||||||
| 445 | } | ||||||
| 446 | return $doc ? $doc : $DOC; | ||||||
| 447 | } | ||||||
| 448 | |||||||
| 449 | sub cloneDOC { | ||||||
| 450 | my $self = shift; | ||||||
| 451 | my $clone = $self->document->cloneNode(1); | ||||||
| 452 | return bless([$clone], __PACKAGE__); | ||||||
| 453 | } | ||||||
| 454 | |||||||
| 455 | sub decode_html { return decode_entities($_[1]); } | ||||||
| 456 | sub parser { return shift->{parser}; } | ||||||
| 457 | |||||||
| 458 | ###custom internal functions | ||||||
| 459 | ###copied from jQuery.js | ||||||
| 460 | sub body { return shift->getElementsByTagName('body'); } | ||||||
| 461 | |||||||
| 462 | sub makeArray { | ||||||
| 463 | my ( $array, $results ) = @_; | ||||||
| 464 | my $ret = $results || []; | ||||||
| 465 | if ( ref $array eq 'ARRAY' ) { | ||||||
| 466 | push @{$ret}, @{$array}; | ||||||
| 467 | } | ||||||
| 468 | else { $ret = \@_; } | ||||||
| 469 | return wantarray | ||||||
| 470 | ? @$ret | ||||||
| 471 | : $ret; | ||||||
| 472 | } | ||||||
| 473 | |||||||
| 474 | sub merge { | ||||||
| 475 | my ( $first, $second ) = @_; | ||||||
| 476 | my $i = _length($first); | ||||||
| 477 | my $j = 0; | ||||||
| 478 | if ( _length($second) ) { | ||||||
| 479 | for ( my $l = _length($second); $j < $l; $j++ ) { | ||||||
| 480 | $first->[ $i++ ] = $second->[ $j ]; | ||||||
| 481 | } | ||||||
| 482 | } else { | ||||||
| 483 | while ( $second->[$j] ) { | ||||||
| 484 | $first->[ $i++ ] = $second->[ $j++ ]; | ||||||
| 485 | } | ||||||
| 486 | } | ||||||
| 487 | $i = _length($first); | ||||||
| 488 | return wantarray | ||||||
| 489 | ? @$first | ||||||
| 490 | : $first ; | ||||||
| 491 | } | ||||||
| 492 | |||||||
| 493 | sub _length { | ||||||
| 494 | if (ref $_[0] eq 'ARRAY'){ | ||||||
| 495 | @_ = @{$_[0]}; | ||||||
| 496 | } | ||||||
| 497 | return $#_ + 1; | ||||||
| 498 | } | ||||||
| 499 | |||||||
| 500 | sub isDisconnected { | ||||||
| 501 | my $node = shift; | ||||||
| 502 | return !$node || !$node->parentNode || $node->parentNode->nodeType == 11; | ||||||
| 503 | } | ||||||
| 504 | |||||||
| 505 | sub unique { | ||||||
| 506 | my %hash; | ||||||
| 507 | my @ele = grep{!$hash{$$_}++} @_; | ||||||
| 508 | return @ele; | ||||||
| 509 | } | ||||||
| 510 | |||||||
| 511 | sub nodeName { | ||||||
| 512 | my ( $elem, $name ) = @_; | ||||||
| 513 | return $elem->nodeName && uc $elem->nodeName eq uc $name; | ||||||
| 514 | } | ||||||
| 515 | |||||||
| 516 | package jQuery::Obj; | ||||||
| 517 | use base 'jQuery'; | ||||||
| 518 | |||||||
| 519 | ##hack Libxml modules to use jQuery as base module | ||||||
| 520 | ##is this a bad practice? | ||||||
| 521 | package XML::LibXML::NodeList; | ||||||
| 522 | use base 'jQuery::Obj'; | ||||||
| 523 | |||||||
| 524 | package XML::LibXML::Node; | ||||||
| 525 | use base 'jQuery::Obj'; | ||||||
| 526 | |||||||
| 527 | package XML::LibXML::Element; | ||||||
| 528 | use base 'jQuery::Obj'; | ||||||
| 529 | |||||||
| 530 | package XML::LibXML::Text; | ||||||
| 531 | use base 'jQuery::Obj'; | ||||||
| 532 | use base 'XML::LibXML::Element'; | ||||||
| 533 | |||||||
| 534 | 1; | ||||||
| 535 | |||||||
| 536 | __END__ |