File Coverage

blib/lib/JavaScript/Packer.pm
Criterion Covered Total %
statement 338 359 94.1
branch 104 130 80.0
condition 29 33 87.8
subroutine 25 25 100.0
pod 1 3 33.3
total 497 550 90.3


line stmt bran cond sub pod time code
1             package JavaScript::Packer;
2              
3 4     4   190043 use 5.008009;
  4         34  
4 4     4   18 use warnings;
  4         7  
  4         102  
5 4     4   19 use strict;
  4         7  
  4         82  
6 4     4   16 use Carp;
  4         6  
  4         216  
7 4     4   1635 use Regexp::RegGrp;
  4         15159  
  4         4655  
8              
9             # =========================================================================== #
10              
11             our $VERSION = "2.10";
12              
13             our @BOOLEAN_ACCESSORS = ( 'no_compress_comment', 'remove_copyright' );
14              
15             our @COPYRIGHT_ACCESSORS = ( 'copyright', 'copyright_comment' );
16              
17             our @COMPRESS_OPTS = ( 'clean', 'obfuscate', 'shrink', 'best' );
18             our $DEFAULT_COMPRESS = 'clean';
19              
20             our $PACKER_COMMENT = '\/\*\s*JavaScript::Packer\s*(\w+)\s*\*\/';
21             our $COPYRIGHT_COMMENT = '\/\*((?>[^\*]|\*[^\/])*copyright(?>[^\*]|\*[^\/])*)\*\/';
22              
23             our $RESTORE_PATTERN = qr~\x01(\d+)\x01~;
24             our $RESTORE_REPLACEMENT = "\x01%d\x01";
25              
26             our $SHRINK_VARS = {
27             BLOCK => qr/(((catch|do|if|while|with|function)\b[^~{};]*(\(\s*[^{};]*\s*\))\s*)?(\{[^{}]*\}))/, # function ( arg ) { ... }
28             ENCODED_BLOCK => qr/~#?(\d+)~/,
29             CALLER => qr/((?>[a-zA-Z0-9_\x24\.]+)\s*\([^\(\)]*\))(?=[,\)])/, # do_something( arg1, arg2 ) as argument of another function call
30             BRACKETS => qr/\{[^{}]*\}|\[[^\[\]]*\]|\([^\(\)]*\)|~[^~]+~/,
31             IDENTIFIER => qr~[a-zA-Z_\x24][a-zA-Z_0-9\\x24]*~,
32             SCOPED => qr/~#(\d+)~/,
33             VARS => qr~\b(?:var|function)\s+((?>[a-zA-Z0-9_\x24]+))~, # var x, funktion blah
34             PREFIX => qr~\x02~,
35             SHRUNK => qr~\x02\d+\b~
36             };
37              
38             our $BASE62_VARS = {
39             WORDS => qr~(\b[0-9a-zA-Z]\b|(?>[a-zA-Z0-9_]{2,}))~,
40             ENCODE10 => 'String',
41             ENCODE36 => 'function(c){return c.toString(36)}',
42             ENCODE62 => q~function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))}~,
43             UNPACK =>
44             q~eval(function(p,a,c,k,e,r){e=%s;if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'%s'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('%s',%s,%d,'%s'.split('|'),0,{}))~
45             };
46              
47             our $DICTIONARY = {
48             STRING1 => qr~"(?>(?:(?>[^"\\]+)|\\.|\\")*)"~,
49             STRING2 => qr~'(?>(?:(?>[^'\\]+)|\\.|\\')*)'~,
50             STRING3 => qr~`(?>(?:(?>[^`\\]+)|\\.|\\`)*)`~,
51             REGEXP => qr~\/(\\[\/\\]|[^*\/])(\\.|[^\/\n\\])*\/[gim]*~,
52             OPERATOR => qr'return|typeof|[\[(\^=,{}:;&|!*?]',
53             CONDITIONAL => qr~\/\*\@\w*|\w*\@\*\/|\/\/\@\w*|\@(?>\w+)~,
54              
55             # single line comments
56             COMMENT1 => qr~(:?)\/\/([\@#])?([^\n]*)?\n~,
57              
58             # multline comments
59             COMMENT2 => qr~\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/~
60             };
61              
62             our $DATA = [
63             { regexp => $DICTIONARY->{STRING1} },
64             { regexp => $DICTIONARY->{STRING2} },
65             { regexp => $DICTIONARY->{STRING3} },
66             { regexp => $DICTIONARY->{CONDITIONAL} },
67             {
68             regexp => '(' . $DICTIONARY->{OPERATOR} . ')\s*(' . $DICTIONARY->{REGEXP} . ')',
69             replacement => sub {
70             return sprintf( "%s%s", $_[0]->{submatches}->[0], $_[0]->{submatches}->[1] );
71             },
72             },
73             ];
74              
75             our $COMMENTS = [
76             {
77             regexp => ';;;[^\n]*\n',
78             replacement => ''
79             },
80             { regexp => $DICTIONARY->{COMMENT1} . '\s*(' . $DICTIONARY->{REGEXP} . ')?', },
81             { regexp => '(' . $DICTIONARY->{COMMENT2} . ')\s*(' . $DICTIONARY->{REGEXP} . ')?' }
82             ];
83              
84             our $CLEAN = [
85             {
86             regexp => '\(\s*([^;)]*)\s*;\s*([^;)]*)\s*;\s*([^;)]*)\)',
87             replacement => sub { return sprintf( "(%s;%s;%s)", @{ $_[0]->{submatches} } ); }
88             },
89             { regexp => 'throw[^};]+[};]' },
90             {
91             regexp => ';+\s*([};])',
92             replacement => sub { return $_[0]->{submatches}->[0]; }
93             }
94             ];
95              
96             our $WHITESPACE = [
97             { regexp => '\/\/@[^\n]*\n' },
98             {
99             regexp => '@\s+\b',
100             replacement => '@ '
101             },
102             {
103             regexp => '\b\s+@',
104             replacement => ' @'
105             },
106             {
107             regexp => '(\d)\s+(\.\s*[a-z\x24_\[(])',
108             replacement => sub { return sprintf( "%s %s", @{ $_[0]->{submatches} } ); }
109             },
110             {
111             regexp => '([+-])\s+([+-])',
112             replacement => sub { return sprintf( "%s %s", @{ $_[0]->{submatches} } ); }
113             },
114             {
115             regexp => '(?>\s+)(\x24)(?>\s+)',
116             replacement => sub { return sprintf( " %s ", $_[0]->{submatches}->[0] ); }
117             },
118             {
119             regexp => '(\x24)(?>\s+)(?!=)',
120             replacement => sub { return sprintf( "%s ", $_[0]->{submatches}->[0] ); }
121             },
122             {
123             regexp => '(?\s+)(\x24)',
124             replacement => sub { return sprintf( " %s", $_[0]->{submatches}->[0] ); }
125             },
126             {
127             regexp => '\b\s+\b',
128             replacement => ' '
129             },
130             {
131             regexp => '\s+',
132             replacement => ''
133             }
134             ];
135              
136             our $TRIM = [
137             {
138             regexp => '(\d)(?:\|\d)+\|(\d)',
139             replacement => sub { return sprintf( "%d-%d", $_[0]->{submatches}->[0] || 0, $_[0]->{submatches}->[1] || 0 ); }
140             },
141             {
142             regexp => '([a-z])(?:\|[a-z])+\|([a-z])',
143             replacement => sub { return sprintf( "%s-%s", $_[0]->{submatches}->[0], $_[0]->{submatches}->[1] ); }
144             },
145             {
146             regexp => '([A-Z])(?:\|[A-Z])+\|([A-Z])',
147             replacement => sub { return sprintf( "%s-%s", $_[0]->{submatches}->[0], $_[0]->{submatches}->[1] ); }
148             },
149             {
150             regexp => '\|',
151             replacement => ''
152             }
153             ];
154              
155             our @REGGRPS = ( 'comments', 'clean', 'whitespace', 'concat', 'trim', 'data_store', 'concat_store' );
156              
157             # --------------------------------------------------------------------------- #
158              
159             {
160 4     4   34 no strict 'refs';
  4         8  
  4         16413  
161              
162             foreach my $field ( @BOOLEAN_ACCESSORS ) {
163             next if defined *{ __PACKAGE__ . '::' . $field }{CODE};
164              
165             *{ __PACKAGE__ . '::' . $field } = sub {
166 99     99   228 my ( $self, $value ) = @_;
167              
168 99 100       305 $self->{ '_' . $field } = $value ? 1 : undef if ( defined( $value ) );
    100          
169              
170 99         358 return $self->{ '_' . $field };
171             };
172             }
173              
174             foreach my $field ( @COPYRIGHT_ACCESSORS ) {
175             $field = '_' . $field if ( $field eq 'copyright_comment' );
176             next if defined *{ __PACKAGE__ . '::' . $field }{CODE};
177              
178             *{ __PACKAGE__ . '::' . $field } = sub {
179 126     126   1157 my ( $self, $value ) = @_;
180              
181 126 100 66     394 if ( defined( $value ) and not ref( $value ) ) {
182 46         441 $value =~ s/^\s*|\s*$//gs;
183 46         199 $self->{ '_' . $field } = $value;
184             }
185              
186 126         204 my $ret = '';
187              
188 126 100       357 if ( $self->{ '_' . $field } ) {
189 14         50 $ret = '/* ' . $self->{ '_' . $field } . ' */' . "\n";
190             }
191              
192 126         436 return $ret;
193             };
194             }
195              
196             foreach my $reggrp ( @REGGRPS ) {
197             next if defined *{ __PACKAGE__ . '::reggrp_' . $reggrp }{CODE};
198              
199             *{ __PACKAGE__ . '::reggrp_' . $reggrp } = sub {
200 413     413   775 my ( $self ) = shift;
201              
202 413         4789 return $self->{ '_reggrp_' . $reggrp };
203             };
204             }
205             }
206              
207             sub compress {
208 163     163 1 2499 my ( $self, $value ) = @_;
209              
210 163 100       322 if ( defined( $value ) ) {
211 29 100       188 if ( grep( $value eq $_, @COMPRESS_OPTS ) ) {
    50          
212 21         89 $self->{_compress} = $value;
213             }
214             elsif ( !$value ) {
215 0         0 $self->{_compress} = undef;
216             }
217             }
218              
219 163   66     451 $self->{_compress} ||= $DEFAULT_COMPRESS;
220              
221 163         683 return $self->{_compress};
222             }
223              
224             # these variables are used in the closures defined in the init function
225             # below - we have to use globals as using $self within the closures leads
226             # to a reference cycle and thus memory leak, and we can't scope them to
227             # the init method as they may change. they are set by the minify sub
228             our $reggrp_comments;
229             our $reggrp_clean;
230             our $reggrp_whitespace;
231              
232             sub init {
233 31     31 0 1389216 my $class = shift;
234 31         74 my $self = {};
235              
236 31         71 bless( $self, $class );
237              
238 31         136 @{ $self->{clean}->{reggrp_data} } = ( @$DATA, @$CLEAN );
  31         177  
239 31         159 @{ $self->{whitespace}->{reggrp_data} } = ( @$DATA[ 0, 1, 2, 4 ], @$WHITESPACE );
  31         119  
240 31         129 $self->{trim}->{reggrp_data} = $TRIM;
241              
242 31         136 @{ $self->{data_store}->{reggrp_data} } = map {
243 31         109 {
244             regexp => $_->{regexp},
245 792     792   9152 store => sub { return sprintf( "%s", $_[0]->{match} ); },
246             replacement => sub {
247 792     792   70326 return sprintf( $RESTORE_REPLACEMENT, $_[0]->{store_index} );
248             },
249             }
250 155         888 } @$DATA;
251              
252             $self->{data_store}->{reggrp_data}->[-1]->{replacement} = sub {
253 9     9   2198 return sprintf( "%s$RESTORE_REPLACEMENT", $_[0]->{submatches}->[0], $_[0]->{store_index} );
254 31         225 };
255              
256             $self->{data_store}->{reggrp_data}->[-1]->{store} = sub {
257 9     9   138 return $_[0]->{submatches}->[1];
258 31         149 };
259              
260 31         118 @{ $self->{concat_store}->{reggrp_data} } = map {
261 31         105 my $data = $_;
  93         140  
262             {
263             regexp => $data->{regexp},
264             store => sub {
265 3211     3211   42393 my ( $quote, $string, $rest ) = $_[0]->{match} =~ /^(['"`])(.*)(['"`])$/;
266 3211 100       5597 return $_[0]->{match} if ! $rest;
267              
268 3210         5692 return $string;
269             },
270             replacement => sub {
271 3211     3211   207028 my ( $quote, $string, $rest ) = $_[0]->{match} =~ /^(['"`])(.*)(['"`])$/;
272 3211 100       6013 return $_[0]->{match} if ! $rest;
273              
274 3210         11433 return sprintf( "%s$RESTORE_REPLACEMENT%s", $quote, $_[0]->{store_index}, $quote );
275             },
276              
277 93         530 };
278             } @$DATA[ 0, 1, 2 ];
279              
280 31         133 @{ $self->{concat}->{reggrp_data} } = map {
281 31         92 my $quote = $_;
  93         142  
282 93         115 my $restore_pattern = $RESTORE_PATTERN;
283 93         424 $restore_pattern =~ s/\(\\d\+\)/\\d\+/g;
284              
285 93         256 my $regexp = '(' . $quote . $restore_pattern . $quote . ')((?:\+' . $quote . $restore_pattern . $quote . ')+)';
286 93         1819 $regexp = qr/$regexp/;
287              
288             {
289             regexp => $regexp,
290             replacement => sub {
291 16     16   11820 my $submatches = $_[0]->{submatches};
292 16         27 my $ret = $submatches->[0];
293              
294 16         39 my $next_str = '^\+(' . $quote . $restore_pattern . $quote . ')';
295              
296 16         133 while ( my ( $next ) = $submatches->[1] =~ /$next_str/ ) {
297 20         36 chop( $ret );
298 20         42 $ret .= substr( $next, 1 );
299 20         130 $submatches->[1] =~ s/$next_str//;
300             }
301              
302 16         37 return $ret;
303             },
304 93         630 };
305             } ( '"', '\'', '\`' );
306              
307 31         125 @{ $self->{comments}->{reggrp_data} } = ( @$DATA[ 0, 1, 2, 4 ], @$COMMENTS );
  31         113  
308              
309             # single line comment
310             $self->{comments}->{reggrp_data}->[-2]->{replacement} = sub {
311 1836     1836   439446 my $submatches = $_[0]->{submatches};
312              
313 1836 100       4553 if ( $submatches->[0] eq ':' ) {
    100          
    100          
314 1         10 return sprintf( "://%s", $submatches->[2] );
315             }
316             elsif ( $submatches->[1] eq '#' ) {
317 6         15 my $cmnt = sprintf( "%s//%s%s__NEW_LINE__", @{$submatches}[0 .. 2] );
  6         37  
318 6         28 return $cmnt;
319             }
320             elsif ( $submatches->[1] eq '@' ) {
321 4         17 $reggrp_comments->exec( \$submatches->[2] );
322 4         99 $reggrp_clean->exec( \$submatches->[2] );
323 4         867 $reggrp_whitespace->exec( \$submatches->[2] );
324              
325 4         856 return sprintf( "%s//%s%s\n%s", @{$submatches}[0 .. 3] );
  4         30  
326             }
327 1825         5417 return sprintf( "\n%s", $submatches->[3] );
328 31         254 };
329              
330             # multi line comments
331             $self->{comments}->{reggrp_data}->[-1]->{replacement} = sub {
332 178     178   87973 my $submatches = $_[0]->{submatches};
333 178 100       774 if ( $submatches->[0] =~ /^\/\*\@(.*)\@\*\/$/sm ) {
334 7         27 my $cmnt = $1;
335              
336 7         24 $reggrp_comments->exec( \$cmnt );
337 7         200 $reggrp_clean->exec( \$cmnt );
338 7         1197 $reggrp_whitespace->exec( \$cmnt );
339              
340 7         2797 return sprintf( '/*@%s@*/ %s', $cmnt, $submatches->[1] );
341             }
342 171         665 return sprintf( " %s", $submatches->[1] );
343 31         192 };
344              
345 31         150 foreach my $reggrp ( @REGGRPS ) {
346 217         108189 my $reggrp_args = { reggrp => $self->{$reggrp}->{reggrp_data} };
347              
348 217 100 100     972 $reggrp_args->{restore_pattern} = $RESTORE_PATTERN if ( $reggrp eq 'data_store' or $reggrp eq 'concat_store' );
349              
350 217         580 $self->{ '_reggrp_' . $reggrp } = Regexp::RegGrp->new( $reggrp_args );
351             }
352              
353 31         8256 $self->{block_data} = [];
354              
355 31         495 return $self;
356             }
357              
358             sub minify {
359 42     42 0 12691 my ( $self, $input, $opts );
360              
361 42 100 66     342 unless (ref( $_[0] )
362             and ref( $_[0] ) eq __PACKAGE__ )
363             {
364 9         47 $self = __PACKAGE__->init();
365              
366 9 50       52 shift( @_ ) unless ( ref( $_[0] ) );
367              
368 9         40 ( $input, $opts ) = @_;
369             }
370             else {
371 33         102 ( $self, $input, $opts ) = @_;
372             }
373              
374 42 50       183 if ( ref( $input ) ne 'SCALAR' ) {
375 0         0 carp( 'First argument must be a scalarref!' );
376 0         0 return undef;
377             }
378              
379 42         98 my $javascript = \'';
380 42         87 my $cont = 'void';
381              
382 42 50       152 if ( defined( wantarray ) ) {
383 0 0       0 my $tmp_input = ref( $input ) ? ${$input} : $input;
  0         0  
384              
385 0         0 $javascript = \$tmp_input;
386 0         0 $cont = 'scalar';
387             }
388             else {
389 42 50       144 $javascript = ref( $input ) ? $input : \$input;
390             }
391              
392 42 100       157 if ( ref( $opts ) eq 'HASH' ) {
393 27         78 foreach my $field ( @BOOLEAN_ACCESSORS ) {
394 54 100       157 $self->$field( $opts->{$field} ) if ( defined( $opts->{$field} ) );
395             }
396              
397 27         59 foreach my $field ( 'compress', 'copyright' ) {
398 54 100       263 $self->$field( $opts->{$field} ) if ( defined( $opts->{$field} ) );
399             }
400             }
401              
402             # (re)initialize variables used in the closures
403 42         194 $reggrp_comments = $self->reggrp_comments;
404 42         195 $reggrp_clean = $self->reggrp_clean;
405 42         130 $reggrp_whitespace = $self->reggrp_whitespace;
406              
407 42         115 my $copyright_comment = '';
408              
409 42 100       74 if ( ${$javascript} =~ /$COPYRIGHT_COMMENT/ism ) {
  42         3505  
410 3         35 $copyright_comment = $1;
411             }
412              
413             # Resets copyright_comment() if there is no copyright comment
414 42         220 $self->_copyright_comment( $copyright_comment );
415              
416 42 100 100     221 if ( not $self->no_compress_comment() and ${$javascript} =~ /$PACKER_COMMENT/ ) {
  37         698  
417 1         5 my $compress = $1;
418 1 50       8 if ( $compress eq '_no_compress_' ) {
419 1 50       4 return ${$javascript} if ( $cont eq 'scalar' );
  0         0  
420 1         6 return;
421             }
422              
423 0         0 $self->compress( $compress );
424             }
425              
426 41         70 ${$javascript} =~ s/\r//gsm;
  41         166  
427 41         73 ${$javascript} .= "\n";
  41         752  
428 41         169 $self->reggrp_comments()->exec( $javascript );
429 41         11306 $self->reggrp_clean()->exec( $javascript );
430 41         12765 $self->reggrp_whitespace()->exec( $javascript );
431 41         366900 $self->reggrp_concat_store()->exec( $javascript );
432 41         3915 $self->reggrp_concat()->exec( $javascript );
433 41         17905 $self->reggrp_concat_store()->restore_stored( $javascript );
434              
435 41 100       20392 if ( $self->compress() ne 'clean' ) {
436 17         64 $self->reggrp_data_store()->exec( $javascript );
437              
438 17         1937 while ( ${$javascript} =~ /$SHRINK_VARS->{BLOCK}/ ) {
  44         3560  
439 27         65 ${$javascript} =~ s/$SHRINK_VARS->{BLOCK}/$self->_store_block_data( $1 )/egsm;
  27         3514  
  511         1087  
440             }
441              
442 17         76 $self->_restore_data( $javascript, 'block_data', $SHRINK_VARS->{ENCODED_BLOCK} );
443              
444 17         44 my %shrunk_vars = map { $_ => 1 } ( ${$javascript} =~ /$SHRINK_VARS->{SHRUNK}/g );
  1657         2486  
  17         925  
445              
446 17         232 my $cnt = 0;
447 17         105 foreach my $shrunk_var ( sort keys( %shrunk_vars ) ) {
448 58         95 my $short_id;
449             do {
450 70         196 $short_id = $self->_encode52( $cnt++ );
451 58         63 } while ( ${$javascript} =~ /[^a-zA-Z0-9_\\x24\.]\Q$short_id\E[^a-zA-Z0-9_\\x24:]/ );
  70         2706  
452              
453 58         107 ${$javascript} =~ s/$shrunk_var/$short_id/g;
  58         1642  
454             }
455              
456 17         79 $self->reggrp_data_store()->restore_stored( $javascript );
457              
458 17         5427 $self->{block_data} = [];
459             }
460              
461 41 100 100     160 if ( $self->compress() eq 'obfuscate' or $self->compress() eq 'best' ) {
462 7         21 my $words = {};
463              
464 7         15 my @words = ${$javascript} =~ /$BASE62_VARS->{WORDS}/g;
  7         9066  
465              
466 7         83 my $idx = 0;
467              
468 7         30 foreach ( @words ) {
469 10898         17415 $words->{$_}->{count}++;
470             }
471              
472 7         27 WORD: foreach my $word ( sort { $words->{$b}->{count} <=> $words->{$a}->{count} } sort keys( %{$words} ) ) {
  10419         14287  
  7         1597  
473              
474 3122 100 66     8506 if ( exists( $words->{$word}->{encoded} ) and $words->{$word}->{encoded} eq $word ) {
475 151         223 next WORD;
476             }
477              
478 2971         4000 my $encoded = $self->_encode62( $idx );
479              
480 2971 100       5443 if ( exists( $words->{$encoded} ) ) {
481 209         254 my $next = 0;
482 209 100       451 if ( exists( $words->{$encoded}->{encoded} ) ) {
483 58         146 $words->{$word}->{encoded} = $words->{$encoded}->{encoded};
484 58         99 $words->{$word}->{index} = $words->{$encoded}->{index};
485 58         102 $words->{$word}->{minus} = length( $word ) - length( $words->{$word}->{encoded} );
486 58         74 $next = 1;
487             }
488 209         301 $words->{$encoded}->{encoded} = $encoded;
489 209         338 $words->{$encoded}->{index} = $idx;
490 209         269 $words->{$encoded}->{minus} = 0;
491 209         269 $idx++;
492 209 100       374 next WORD if ( $next );
493 151         210 redo WORD;
494             }
495              
496 2762         3755 $words->{$word}->{encoded} = $encoded;
497 2762         3226 $words->{$word}->{index} = $idx;
498 2762         3556 $words->{$word}->{minus} = length( $word ) - length( $encoded );
499              
500 2762         3794 $idx++;
501             }
502              
503 7         349 my $packed_length = length( ${$javascript} );
  7         43  
504              
505 7         22 my ( @pk, @pattern ) = ( (), () );
506              
507 7         18 foreach ( sort { $words->{$a}->{index} <=> $words->{$b}->{index} } sort keys( %{$words} ) ) {
  12076         17624  
  7         1738  
508 2971         6972 $packed_length -= ( $words->{$_}->{count} * $words->{$_}->{minus} );
509              
510 2971 100       4623 if ( $words->{$_}->{encoded} ne $_ ) {
511 2762         3532 push( @pk, $_ );
512 2762         5406 push( @pattern, $words->{$_}->{encoded} );
513             }
514             else {
515 209         282 push( @pk, '' );
516 209         293 push( @pattern, '' );
517             }
518             }
519              
520 7         368 my $size = scalar( @pattern );
521              
522 7 100       176 splice( @pattern, 62 ) if ( scalar( @pattern ) > 62 );
523              
524 7         51 my $pd = join( '|', @pattern );
525              
526 7         51 $self->reggrp_trim()->exec( \$pd );
527              
528 7 50       909 unless ( $pd ) {
529 0         0 $pd = '^$';
530             }
531             else {
532 7         28 $pd = '[' . $pd . ']';
533              
534 7 100       28 if ( $size > 62 ) {
535 3         10 $pd = '(' . $pd . '|';
536              
537 3         21 my $enc = $self->_encode62( $size );
538              
539 3         16 my ( $c ) = $enc =~ /(^.)/;
540 3         6 my $ord = ord( $c );
541              
542 3         10 my $mul = length( $enc ) - 1;
543              
544 3         10 my $is62 = 0;
545              
546 3 100       22 if ( $ord >= 65 ) {
    100          
    50          
    50          
547 1 50       9 if ( $c eq 'Z' ) {
548 0         0 $mul += 1;
549 0         0 $is62 = 1;
550             }
551             else {
552 1         4 $pd .= '[0-9a';
553 1 50       9 if ( $ord > 97 ) {
    50          
    50          
554 0         0 $pd .= '-' . $c;
555             }
556             elsif ( $ord > 65 ) {
557 0         0 $pd .= '-zA-' . $c;
558             }
559             elsif ( $ord == 65 ) {
560 1         3 $pd .= '-zA';
561             }
562 1         3 $pd .= ']';
563             }
564             }
565             elsif ( $ord == 57 ) {
566 1         3 $pd .= '[0-9]';
567             }
568             elsif ( $ord == 50 ) {
569 0         0 $pd .= '[12]';
570             }
571             elsif ( $ord == 49 ) {
572 1         3 $pd .= '1';
573             }
574             else {
575 0         0 $pd .= '[0-' . ( $ord - 48 ) . ']';
576             }
577              
578 3 50       15 $pd .= '[0-9a-zA-Z]' . ( ( $mul > 1 ) ? '{' . $mul . '}' : '' );
579              
580 3 50       11 $mul-- if ( $is62 );
581              
582 3 50       12 if ( $mul > 1 ) {
583 0         0 for ( my $i = $mul; $i >= 2; $i-- ) {
584 0         0 $pd .= '|[0-9a-zA-Z]{' . $i . '}';
585             }
586             }
587              
588 3         10 $pd .= ')';
589             }
590             }
591 7         25 $packed_length += length( $pd );
592              
593 7         639 my $pk = join( '|', @pk );
594 7         338 $pk =~ s/(?>\|+)$//;
595 7         19 $packed_length += length( $pk );
596              
597 7 50       64 my $pc = length( $pk ) ? ( ( $pk =~ tr/|/|/ ) + 1 ) : 0;
598 7         22 $packed_length += length( $pc );
599              
600 7         15 my $pa = '[]';
601 7         15 $packed_length += length( $pa );
602              
603 7 50       42 my $pe = $BASE62_VARS->{ 'ENCODE' . ( $pc > 10 ? $pc > 36 ? 62 : 36 : 10 ) };
    100          
604 7         14 $packed_length += length( $pe );
605              
606 7         20 $packed_length += length( $BASE62_VARS->{UNPACK} );
607 7         109 $packed_length -= ( $BASE62_VARS->{UNPACK} =~ s/(%s|%d)/$1/g ) * 2;
608              
609 7         21 my ( @length_matches ) = ${$javascript} =~ s/((?>[\r\n]+))/$1/g;
  7         284  
610 7         29 foreach ( @length_matches ) {
611 7         38 $packed_length -= length( $_ ) - 3;
612             }
613              
614 7         14 $packed_length += ${$javascript} =~ tr/\\\'/\\\'/;
  7         77  
615              
616 7 100 100     34 if ( $self->compress() eq 'obfuscate' or $packed_length <= length( ${$javascript} ) ) {
  5         50  
617              
618 5         12 ${$javascript} =~ s/$BASE62_VARS->{WORDS}/sprintf( "%s", $words->{$1}->{encoded} )/eg;
  5         48  
  10865         28962  
619              
620 5         17 ${$javascript} =~ s/([\\'])/\\$1/g;
  5         395  
621 5         13 ${$javascript} =~ s/[\r\n]+/\\n/g;
  5         103  
622              
623 5         9 my $pp = ${$javascript};
  5         31  
624              
625 5         178 ${$javascript} = sprintf( $BASE62_VARS->{UNPACK}, $pe, $pd, $pp, $pa, $pc, $pk );
  5         3733  
626             }
627              
628             }
629              
630 41 100       194 if ( not $self->remove_copyright() ) {
631 40   100     121 ${$javascript} = ( $self->copyright() || $self->_copyright_comment() ) . ${$javascript};
  40         131  
  40         1313  
632             }
633              
634             # GH #9 bodge for sourceMappingURL
635 41         138 ${$javascript} =~ s/__NEW_LINE__/\n/xsmg;
  41         308  
636 41         64 ${$javascript} =~ s!//#sourceMappingURL!//# sourceMappingURL!g;
  41         514  
637 41         87 chomp( ${$javascript} );
  41         162  
638              
639 41 50       1484 return ${$javascript} if ( $cont eq 'scalar' );
  0         0  
640             }
641              
642             sub _restore_data {
643 379     379   626 my ( $self, $string_ref, $data_name, $pattern ) = @_;
644              
645 379         440 while ( ${$string_ref} =~ /$pattern/ ) {
  533         1900  
646 154         210 ${$string_ref} =~ s/$pattern/$self->{$data_name}->[$1]/egsm;
  154         707  
  511         2104  
647             }
648             }
649              
650             sub _store_block_data {
651 511     511   1363 my ( $self, $match ) = @_;
652              
653 511         3831 my ( undef, $prefix, $blocktype, $args, $block ) = $match =~ /$SHRINK_VARS->{BLOCK}/;
654              
655 511   100     1192 $prefix ||= '';
656 511   100     925 $blocktype ||= '';
657 511   100     835 $args ||= '';
658 511         597 my $replacement = '';
659 511 100       826 if ( $blocktype eq 'function' ) {
660              
661 181         460 $self->_restore_data( \$block, 'block_data', $SHRINK_VARS->{SCOPED} );
662              
663 181         1420 $args =~ s/\s*//g;
664              
665 181         431 $block = $args . $block;
666 181         919 $prefix =~ s/$SHRINK_VARS->{BRACKETS}//;
667              
668 181         665 $args =~ s/^\(|\)$//g;
669              
670 181         543 while ( $args =~ /$SHRINK_VARS->{CALLER}/ ) {
671 0         0 $args =~ s/$SHRINK_VARS->{CALLER}//gsm;
672             }
673              
674 181         709 my @vars = grep( $_, split( /\s*,\s*/, $args ) );
675 181 100       423 my $do_shrink = grep( $_ eq '_no_shrink_', @vars ) ? 0 : 1;
676              
677 181         240 my %block_vars = ();
678 181 100       284 if ( $do_shrink ) {
679 180         1928 %block_vars = map { $_ => 1 } ( $block =~ /$SHRINK_VARS->{VARS}/g ), grep( $_ ne '$super', @vars );
  475         1186  
680             }
681              
682 181         540 $self->_restore_data( \$block, 'block_data', $SHRINK_VARS->{ENCODED_BLOCK} );
683              
684 181 100       336 if ( $do_shrink ) {
685              
686 180         216 my $cnt = 0;
687 180         683 foreach my $block_var ( sort keys( %block_vars ) ) {
688 468 50       726 if ( length( $block_var ) ) {
689 468         3854 while ( $block =~ /$SHRINK_VARS->{PREFIX}\Q$cnt\E\b/ ) {
690 16         127 $cnt++;
691             }
692              
693 468         8956 while ( $block =~ /[^a-zA-Z0-9_\\x24\.]\Q$block_var\E[^a-zA-Z0-9_\\x24:]/ ) {
694 491         8826 $block =~ s/([^a-zA-Z0-9_\\x24\.])\Q$block_var\E([^a-zA-Z0-9_\\x24:])/sprintf( "%s\x02%d%s", $1, $cnt, $2 )/eg;
  1656         7744  
695             }
696              
697 468         5095 $block =~ s/([^{,a-zA-Z0-9_\\x24\.])\Q$block_var\E:/sprintf( "%s\x02%d:", $1, $cnt )/eg;
  1         5  
698              
699 468         970 $cnt++;
700             }
701             }
702             }
703 181         291 $replacement = sprintf( "%s~%d~", $prefix, scalar( @{ $self->{block_data} } ) );
  181         577  
704              
705 181         227 push( @{ $self->{block_data} }, $block );
  181         550  
706             }
707             else {
708 330         361 $replacement = sprintf( "~#%d~", scalar( @{ $self->{block_data} } ) );
  330         763  
709              
710 330         423 push( @{ $self->{block_data} }, $prefix . $block );
  330         728  
711             }
712              
713 511         17275 return $replacement;
714             }
715              
716             sub _encode52 {
717 70     70   126 my ( $self, $c ) = @_;
718              
719 70         95 my $m = $c % 52;
720              
721 70 50       166 my $ret = $m > 25 ? chr( $m + 39 ) : chr( $m + 97 );
722              
723 70 50       160 if ( $c >= 52 ) {
724 0         0 $ret = $self->_encode52( int( $c / 52 ) ) . $ret;
725             }
726              
727 70 50       118 $ret = substr( $ret, 1 ) . '0' if ( $ret =~ /^(do|if|in)$/ );
728              
729 70         147 return $ret;
730             }
731              
732             sub _encode62 {
733 5725     5725   6796 my ( $self, $c ) = @_;
734              
735 5725         6261 my $m = $c % 62;
736              
737 5725 100       8853 my $ret = $m > 35 ? chr( $m + 29 ) : $m > 9 ? chr( $m + 87 ) : $m;
    100          
738              
739 5725 100       7898 if ( $c >= 62 ) {
740 2751         4314 $ret = $self->_encode62( int( $c / 62 ) ) . $ret;
741             }
742              
743 5725         10048 return $ret;
744             }
745              
746             1;
747              
748             __END__