File Coverage

blib/lib/WWW/Scripter/Plugin/JavaScript/JE.pm
Criterion Covered Total %
statement 145 159 91.1
branch 52 58 89.6
condition 23 26 88.4
subroutine 29 34 85.2
pod 3 9 33.3
total 252 286 88.1


line stmt bran cond sub pod time code
1             package WWW::Scripter::Plugin::JavaScript::JE;
2              
3 4     4   16 use strict; # :-(
  4         3  
  4         95  
4 4     4   13 use warnings; # :-(
  4         4  
  4         103  
5              
6 4     4   27 use Carp 'croak';
  4         4  
  4         168  
7 4     4   15 use Hash::Util::FieldHash::Compat 'fieldhash';
  4         3  
  4         26  
8 4     4   193 use HTML::DOM::Interface 0.032 ':all'; # for the constants (0.032
  4         83  
  4         523  
9 4     4   1412 use JE'Destroyer; # for UTF16)
  4         1526  
  4         94  
10 4     4   2057 use JE 0.038; # call_with
  4         220448  
  4         121  
11 4     4   24 use Scalar::Util 1.09 qw'weaken refaddr';
  4         42  
  4         6796  
12              
13             our $VERSION = '0.009';
14             our @ISA = 'JE';
15              
16             fieldhash my %parathia;
17              
18             # No need to implement eval since JE's method
19             # is sufficient
20              
21             my @types;
22             $types[BOOL] = Boolean =>;
23             $types[STR ] = DOMString =>;
24             $types[NUM ] = Number =>;
25             $types[OBJ ] = null =>;
26              
27             sub new {
28 26     26 1 31 my $self = SUPER::new{shift} html_mode => 1;
  26         111  
29 26         634 weaken( $parathia{$self} = my $parathi = shift );
30            
31 26         49 my $i = \%WWW'Scripter'WindowInterface;
32 26   100     2859 for(grep !/^_/ && $$i{$_} & METHOD, =>=> keys %$i) {
33 468         5738 my $method = $_;
34 468         458 my $type = $$i{$_};
35             $self->new_method($_ => sub {
36 6     6   175 my $parathi = $parathia{my $self = shift};
37             # undocumented JE methods:
38 6         31 $self->_cast(
39             scalar
40             $parathi->$method($self->_unwrap(@_)),
41             $types[$type&TYPE]
42             );
43 468         1727 });
44             }
45 26   100     2861 for(grep !/^_/ && !($$i{$_}&METHOD) =>=> keys %$i) {
46 1196         18754 my $name = $_;
47 1196 100       2067 next if $name =~ /^(?:window|self)\z/; # for efficiency
48 1144         869 my $type = $$i{$_};
49 1144 100       1184 if ($type & READONLY) {
50             $self->prop({
51             name => $_,
52             readonly => 1,
53             fetch => sub {
54 34     34   338 my $self = shift;
55 34         154 $parathia{$self}->$name;
56             $self->_cast(
57             scalar
58 34         257 $parathia{$self}->$name,
59             $types[$type&TYPE]
60             );
61             },
62 208         747 });
63             }
64             else {
65             $self->prop({
66             name => $_,
67             fetch => sub {
68 2     2   17 my $self = shift;
69             $self->_cast(
70             scalar
71 2         10 $parathia{$self}->$name,
72             $types[$type&TYPE]
73             );
74             },
75             store => sub {
76 0     0   0 my $self = shift;
77             $self->_cast(
78             scalar
79 0         0 $parathia{$self}
80             ->$name(shift),
81             $types[$type&TYPE]
82             );
83             },
84 936         3960 });
85             }
86             }
87              
88             # ~~~ This is *such* a hack! If anyone wants to help me fix JE’s
89             # screwed up type-conversion system (which makes my head hurt),
90             # please let me know.
91             $self->new_function("DOMString", sub {
92 10 100   10   348 if(ref($_[0]) =~ /^JE::(?:Null|Undefined)\z/) {
93 2         6 return $_[0]->global->null;
94             }
95 8         22 return $_[0]->to_string;
96 26         546 });
97              
98             $self->bind_class(
99             package => 'WWW::Scripter',
100             wrapper => sub {
101 9     9   2728 my ($self, $window) = @_;
102              
103             # ~~~ This needs to be modified to create a special
104             # restrictive wrapper if the $window has a
105             # different origin.
106             # Fetch the cached JavaScript environment corres-
107             # ponding to the WWW::Scripter object.
108             #warn $window, " ", $window->response, " ", $window->uri;
109 9 100       35 refaddr
110             $window->plugin("JavaScript")->back_end($window)
111             == refaddr $self
112             and return $self;
113 7         65 (__PACKAGE__."::Proxy")->new($window)
114             },
115 26         492 );
116             $self->bind_class(
117             package => 'WWW::Scripter::Frames',
118             wrapper => sub {
119 1     1   22 my($self,$frames) = @_;
120 1         4 return $self->upgrade($frames->window);
121             },
122 26         7531 );
123             # ~~~ We also need a 'JE' wrapper, that will create a special
124             # objcet that delegates to the JS environment currently
125             # belonging to the window.
126              
127 26         258 for my $p($self->{String}{prototype}) {
128 26         364 for my $v(
129             ['anchor','a','name'],
130             ['fontcolor','font','color'],
131             ['fontsize','font','size'],
132             ['link','a','href'],
133             ) {
134             $p->new_method(
135             $$v[0],
136             sub{
137 8 100   8   4040 qq'<$$v[1] $$v[2]="'
138             . (defined $_[1] ? $_[1] : 'undefined')
139             . qq'">$_[0]'
140             }
141             )
142 104         12748 }
143 26         3880 for my $v('big', 'blink', 'small','strike','sub','sup') {
144 6     6   2761 $p->new_method( $v, sub{"<$v>$_[0]"} )
145 156         19975 }
146 26         3968 for my $v(['bold','b'],['fixed','tt'],['italics','i'],) {
147             $p->new_method(
148             $$v[0],
149 3     3   1397 sub{"<$$v[1]>$_[0]"}
150 78         8199 );
151             }
152             }
153              
154             # for speed:
155 26         4021 $self->prop('frames' => $self);
156 26         50 $self->prop('window' => $self);
157 26         53 $self->prop('self' => $self);
158             }
159              
160             sub prop {
161 11387     11387 1 752055 my $self = shift;
162 11387 100       23415 return $self->SUPER::prop(@_) if ref $_[0] eq 'HASH';
163            
164 6554         11376 my $val = $self->SUPER::prop(@_);
165 6554 100       203599 return $val if defined $val;
166              
167 2435         2156 my $name = shift;
168 2435 50       3815 return $_[0] if @_;
169              
170 2435         3576 my $window = $parathia{$self};
171              
172             my $ret =
173             $name =~ /^(?:0|[1-9]\d*)\z/ && $name < 4294967295
174             ? $window->frames->[$name]
175 2435 100 66     11233 : $window->frames->{$name};
176 2435 100       74794 defined $ret ? $self->upgrade($ret) : $ret;
177             }
178              
179             sub set {
180 3     3 0 318 my $obj = shift;
181 3         5 my $val = pop;
182 3 50       9 croak "Not enough arguments for W:M:P:JS:JE->set" unless @_;
183 3         7 my $prop = pop;
184 3         5 for (@_) {
185 1         3 my $next_obj = $obj->{$_};
186             defined $next_obj or
187             $obj->{$_} = {},
188 1 50       5 $obj = $obj->{$_}, next;
189 0         0 $obj = $next_obj;
190             }
191 3         8 $obj->{$prop} = $val;
192 3         55 return;
193             }
194              
195             sub bind_classes {
196 123     123 0 172 my($self, $classes) = @_;
197 123         107 my @defer;
198 123         2043 for (grep /::/, keys %$classes) {
199 2515         12445 my $i = $$classes{$$classes{$_}}; # interface info
200             my @args = (
201             unwrap => 1,
202             package => $_,
203             name => $$classes{$_},
204             methods => { map {
205 3242         2255 my $perlname = $_;
206 3242 100       4001 $perlname .= 16 if $$i{$_} & UTF16;
207 3242 100       29375 $_ => $$i{$_} & VOID
208             ? $perlname
209             : "$perlname:$types[$$i{$_} & TYPE]"
210             } grep !/^_/ && $$i{$_} & METHOD, keys %$i },
211             props => { map {
212 15410         9567 my $perlname = $_;
213 15410 100       17964 $perlname .= 16 if $$i{$_} & UTF16;
214 15410 100       41265 $$i{$_} & READONLY
215             ? ($_ =>{
216             fetch=>
217             "$perlname:$types[$$i{$_} & TYPE]"
218             })
219             : ($_ => "$perlname:$types[$$i{$_} & TYPE]")
220             } grep !/^_/ && !($$i{$_} & METHOD), keys %$i },
221             hash => $$i{_hash},
222             array => $$i{_array},
223             exists $$i{_isa} ? (isa => $$i{_isa}) : (),
224             exists $$i{_constructor}
225             ? (constructor => $$i{_constructor})
226 2515 100 100     58203 : (),
    100 100        
227             );
228 2515         4419 my $make_constants;
229 2515 100       3458 if(exists $$i{_constants}){
230 182         157 my $p = $_;
231 182     182   193 $make_constants = sub { for(@{$$i{_constants}}){
  182         434  
232 1846         86480 /([^:]+\z)/;
233 1846         77706 $self->{$$classes{$p}}{$1} =
234             # ~~~ to be replaced simply with 'eval' when JE's
235             # upgrading is improved:
236             $self->upgrade(eval)->to_number;
237 182         546 }}}
238 2515 100 100     6637 if (exists $$i{_isa} and !exists $self->{$$i{_isa}}) {
239 1040         33139 push @defer, [\@args, $$i{_isa}, $make_constants]
240             } else {
241             # use Data::Dumper; print Dumper \@args if $_ !~ /HTML|CSS/;
242 1475         7364 $self->bind_class(@args);
243 1475 100       559145 defined $make_constants and &$make_constants;
244             }
245             }
246 123         1022 while(@defer) {
247 59         193 my @copy = @defer;
248 59         94 @defer = ();
249 59         100 for (@copy) {
250 1164 100       2673 if(exists $self->{$$_[1]}) { # $$_[1] == superclass
251 1040         5417 $self->bind_class(@{$$_[0]});
  1040         2573  
252 1040 100       335696 &{$$_[2] or next}
  1040         5402  
253             }
254             else {
255 124         3162 push @defer, $_;
256             }
257             }
258             }
259 123         1133 return;
260             }
261              
262             sub event2sub {
263 6     6 0 25 my ($w, $code, $elem, $url, $line) = @_;
264              
265             # ~~~ JE's interface needs to be improved. This is a mess:
266             # We need the line break after $code, because there may be a sin-
267             # gle-line comment at the end, and no line break. ("foo //bar"
268             # would fail without this, because the }) would be com-
269             # mented out too.)
270             # We have to check whether the $elem is a form before calling it’s
271             #‘form’ method, because forms *do* have such a method, but it
272             # returns a list of form element names and values, which is *not*
273             # what we want. (We want the element’s parent form where
274             # applicable.)
275 6 100 100     47 ($w->compile("(function(){ $code\n })",$url,$line)||return)
      100        
276             ->execute($w, bless [
277             $w,
278             $w->upgrade($elem->ownerDocument),
279             $elem->tag ne 'form' && $elem->can('form')
280             ? $w->upgrade($elem->form) : (),
281             my $wrapper=($w->upgrade($elem))
282             ], 'JE::Scope');
283             }
284              
285             sub define_setter {
286 0     0 0 0 my $obj = shift;
287 0         0 my $cref = pop;
288 0         0 my $prop = pop;
289 0         0 for (@_) {
290 0         0 my $next_obj = $obj->{$_};
291             defined $next_obj or
292             $obj->{$_} = {},
293 0 0       0 $obj = $obj->{$_}, next;
294 0         0 $obj = $next_obj;
295             }
296 0     0   0 $obj->prop({name=>$prop, store=>sub{$cref->($_[1])}});
  0         0  
297 0         0 return;
298             }
299              
300             sub new_function {
301 96     96 1 157 my($self, $name, $sub, $type) = @_;
302 96 100       150 if(defined $type) {
303             $self->new_function(
304 2     2   67 $name => sub { $self->{$type}->($sub->(@_)) }
305 18         74 );
306 18         276 weaken $self;
307             } else {
308 78         196 shift->SUPER::new_function(@_);
309             }
310             }
311              
312             sub exists {
313 3107     3107 0 128747 my $self = shift;
314             SUPER::exists $self @_
315 3107 100       5304 or do {
316 1177         4561 my($name) = @_;
317 1177         1503 my $window = $parathia{$self};
318             $name =~ /^(?:0|[1-9]\d*)\z/ && $name < 4294967295
319             ? defined $window->frames->[$name]
320 1177 50 33     4363 : defined $window->frames->{$name}
321             }
322             }
323              
324             sub destroy {
325 28 100   28 0 50977 return SUPER::destroy{@_} if caller eq 'JE::Destroyer';
  14         56  
326 14         38 JE'Destroyer'destroy $_[0]
327             }
328              
329              
330             package WWW::Scripter::Plugin::JavaScript::JE::Proxy;
331              
332             # ~~~ This package needs to override any method used internally by
333             # JavaScript that returns a JE datatype to return a wrapper instead.
334              
335             sub new {
336             # If a method is called on another window, as in frames[0].alert(), an
337             # lvalue is created with the proxy object returned by frames[0] as the
338             # the base and ‘alert’ as the property name. The lvalue then fetches the
339             # value of frames[0].alert and runs it with frames[0] as the invocant.
340             # This means that it is the proxy that gets passed to the closures
341             # in WSPJSJE:new above. Since those expect to find a window in
342             # $parathia{$self}, and $self might be a proxy, we have to add ourselves
343             # to the %parathi field hash and store the window there. For the sake of
344             # speed in autoload, we still keep a direct reference to the window.
345 7     7   14 my $proxy = bless \(my $w = pop);
346 7         57 Scalar'Util'weaken( $parathia{$proxy} = $w );
347 7         34 $proxy
348             }
349              
350             sub AUTOLOAD {
351 22     22   690 my $window = ${;shift};
  22         29  
352             (
353             # We have to use this roundabout method
354             # rather than __PACKAGE__->new($window),
355             # because the JS plugin needs to do its
356             # stuff (binding classes, etc.).
357             $window->plugin("JavaScript")->back_end($window)
358 22         58 )->${\(our $AUTOLOAD =~ /.*::(.+)\z/)[0]}(@_)
  22         197  
359             }
360              
361       0     sub DESTROY{}
362             sub isa {
363 0     0     goto &UNIVERSAL'isa;
364             }
365              
366              
367             =cut
368              
369             # ------------------ DOCS --------------------#
370              
371             =head1 NAME
372              
373             WWW::Scripter::Plugin::JavaScript::JE - JE backend for WSPJS
374              
375             =head1 VERSION
376              
377             0.008 (alpha)
378              
379             =head1 DESCRIPTION
380              
381             This little module is a bit of duct tape to connect the JavaScript plugin
382             for L to the JE JavaScript engine. Don't use this module
383             directly. For usage, see
384             L.
385              
386             =head1 REQUIREMENTS
387              
388             Hash::Util::FieldHash::Compat
389              
390             HTML::DOM 0.032 or later
391              
392             JE 0.056 or higher
393              
394             WWW::Scripter 0.016 or higher
395              
396             =head1 SEE ALSO
397              
398             =over 4
399              
400             =item -
401              
402             L
403              
404             =item -
405              
406             L
407              
408             =item -
409              
410             L
411              
412             =item -
413              
414             L (the original version of this
415             module)
416              
417             =cut