3 use warnings FATAL => 'all', NONFATAL => 'redefine';
6 ###################################################
8 # @author Clinton Gormley
9 # @copyright Copyright 2008, Clinton Gormley, All rights reserved.
11 # This file compresses the TinyMCE JavaScript using GZip and
12 # enables the browser to do two requests instead of one for each .js file.
13 # Notice: This script defaults the button_tile_map option to true for
16 ##################################################
17 our $VERSION = '2.0.3';
19 # Default cache path is subdir to tinymce
20 my $cache_path = 'cache';
22 # Custom extra javascripts to pack
23 my @custom_files = qw();
26 our $cache_for = 3600 * 24 * 10;
29 ##################################################
33 use Digest::MD5 qw(md5_hex);
34 use File::Slurp qw(slurp write_file);
36 my %P = %{ get_params() };
41 ? 'Content-type: text/javascript; charset=' . $P{charset}
42 : 'Content-type: text/javascript';
44 # If this file is requested directly, send the JS compressor file
45 # and init with default settings
46 unless ( $P{js} eq 'true' ) {
47 print headers(@extra_headers);
48 print slurp_file('tiny_mce_gzip.js');
49 print "tinyMCE_GZ.init({})\n";
54 custom_files => \@custom_files,
55 ( map { $_ => [ split( ',', $P{$_} ) ] } qw(plugins languages themes) )
58 ## Check if it supports gzip
59 my $accept_header = $ENV{HTTP_ACCEPT_ENCODING} || '';
60 my ($supports_gzip) = ( $accept_header =~ m/((?:x-)?gzip)\b/i );
61 my $compress = ( $supports_gzip && $P{compress} ne 'false' ) ? 1 : 0;
63 # Get params for cache
65 = File::Spec->file_name_is_absolute($cache_path)
67 : File::Spec->catdir( $cwd, 'cache' );
68 my $cache_key = get_cache_key( \%modules, $P{suffix} );
69 my $js_file = File::Spec->catfile( $cache_path, $cache_key . '.js' );
70 my $gz_file = File::Spec->catfile( $cache_path, $cache_key . '.gz' );
71 my $cache_file = $compress ? $gz_file : $js_file;
73 # Use cached data or generate new?
75 = ( $P{diskcache} eq 'true' && -e $cache_file )
76 ? slurp_file($cache_file)
77 : generate_and_cache_data( \%modules, $compress, $js_file,
78 $gz_file, $P{core}, $P{suffix} );
82 push @extra_headers, "Content-Encoding: $supports_gzip";
85 print headers(@extra_headers);
90 #===================================
92 #===================================
93 my $qs = $ENV{QUERY_STRING} || '';
94 $qs =~ s/(%([0-9a-fA-F]{2,2}))/my $c = hex($2); $c < 256 ? chr($c) : $1/eg;
95 my @raw_params = split( /[&;]+/, $qs );
96 my %parsed = map { $_ => '' } qw(
97 plugins languages themes
99 core suffix charset );
100 while ( my $pair = shift @raw_params ) {
101 my ( $key, $value ) = split( /=/, $pair );
102 next unless exists $parsed{$key};
104 $value =~ tr/0-9a-zA-Z\-_,//cd;
105 $parsed{$key} = $value;
107 $parsed{suffix} = $parsed{suffix} eq '_src' ? '_src' : '';
111 #===================================
113 #===================================
114 my @extra_headers = @_;
115 my $date = scalar( gmtime( time + $cache_for ) ) . " GMT";
116 return <<HEADERS. join( "\n", @extra_headers ) . "\n\n";
117 Vary: Accept-Encoding
123 #===================================
125 #===================================
126 my $file = File::Spec->catfile(@_);
127 unless ( File::Spec->file_name_is_absolute($file) ) {
128 $file = File::Spec->catfile( $cwd, $file );
130 return slurp( $file, binmode => ':raw' );
133 #===================================
135 #===================================
136 return File::Spec->catpath(
137 File::Spec->no_upwards( ( File::Spec->splitpath($0) )[ 0, 1 ] ) );
140 #===================================
142 #===================================
145 my $cache_key = md5_hex(
147 ( map { @{ $modules->{$_} } }
148 qw(plugins languages themes custom_files )
155 ($cache_key) = ( $cache_key =~ /^([0-9a-f]+)$/ );
156 die "Couldn't generate cache key - problem with MD5 libraries?"
162 #===================================
163 sub generate_and_cache_data {
164 #===================================
166 my $compress = shift;
172 # Core file plus langs
173 my @langs = @{ $modules->{languages} };
175 my $js_data = join( '', map { slurp_file( 'langs', "$_.js" ) } @langs );
177 # Themes plus their langs
178 foreach my $theme ( @{ $modules->{themes} } ) {
180 .= slurp_file( 'themes', $theme, "editor_template${suffix}.js" )
182 map { slurp_file( 'themes', $theme, 'langs', "$_.js" ) }
186 # Plugins plus their langs
187 foreach my $plugin ( @{ $modules->{plugins} } ) {
189 .= slurp_file( 'plugins', $plugin, "editor_plugin${suffix}.js" )
194 slurp_file( 'plugins', $plugin, 'langs', "$_.js" );
202 $js_data .= slurp_file($_) for ( @{ $modules->{custom_files} } );
204 # If the core is required, add that too
205 unless ( $core eq 'false' ) {
207 = slurp_file("tiny_mce${suffix}.js")
208 . 'tinyMCE_GZ.start();'
210 . 'tinyMCE_GZ.end();';
214 my $gz_data = Compress::Zlib::memGzip($js_data)
215 or die "Couldn't gzip data";
217 # write files to disk
218 write_file( $js_file, { binmode => ':raw' }, $js_data );
219 write_file( $gz_file, { binmode => ':raw' }, $gz_data );
221 # Choose the correct data to be sent
222 return $compress ? $gz_data : $js_data;
229 TinyMCE Compressor Perl version 2.0.3
233 TinyMCE Compressor gzips all javascript files in TinyMCE to a single
234 streamable file. This makes the overall download size 75% smaller and
235 the number of requests will also be reduced. The overall initialisation
236 time for TinyMCE will be reduced dramatically if you use this script.
238 The Perl fork of the TinyMCE compressor project page is at
239 L<http://github.com/clintongormley/tinymce_compressor>
243 Here is a step by step list on how to install the GZip compressor.
249 Use CPAN to install L<File::Spec>, L<File::Slurp>,
250 L<Digest::MD5> and L<Compress::Zlib>.
252 =item Installing files
254 Copy the tiny_mce_gzip.js and tiny_mce_gzip.pl to the tiny_mce
255 directory. The same directory that contains the tiny_mce.js file.
257 =item Create a cache directory
259 Create the sub directory 'C<cache>' under your tiny_mce
260 directory and give your web server permission to write to it, eg:
265 chmod u+rwx,og-rwx cache
268 B<PLEASE NOTE:> If you upgrade your Tiny MCE editor, you will need to
269 clear out the cache directory.
271 =item Update your code
273 Remove the current script tag:
275 <script type="text/javascript" src="tinymce/jscripts/tiny_mce/tiny_mce.js"></script>
279 <script type="text/javascript" src="tinymce/jscripts/tiny_mce/tiny_mce_gzip.js"></script>
281 Add the new GZip initialization call (see below) that will
282 tell the compressor what to include in the output. This should be the
283 sum of all themes, plugins and languages contained on page.
287 =head2 Running under mod_perl
289 You either need to set up your web server to execute
290 C<tiny_mce_gzip.pl> as a CGI script, or you can configure Apache to run
291 it under mod_perl, which will greatly speed up the response.
293 To do this, you could use a configuration like this:
295 <Location /tiny_mce/tiny_mce_gzip.pl>
296 SetHandler perl-script
297 PerlResponseHandler ModPerl::Registry
298 PerlOptions +ParseHeaders
304 =head2 Example of configuration
306 The example below will pack both themes and all plugins into one
307 file/stream. Remove the things you don't need or add you custom plugins
308 to the settings below. Remember that the tinyMCE_GZ.init call must be
309 placed in B<it's own script tag.>
311 <script type="text/javascript" src="tinymce/jscripts/tiny_mce/tiny_mce_gzip.js"></script>
312 <script type="text/javascript">
314 plugins : 'style,layer,...etc',
315 themes : 'simple,advanced',
321 <!-- Needs to be seperate script tags! -->
323 <script type="text/javascript">
325 .. your normal init ..
329 =head2 Troubleshooting
335 The GZip compressor can fail to load if the server has odd settings or
336 is missing the required support for it to function. To see compilation
337 errors or other problems we suggest that you use HTTP debugging tools
338 like HTTP Fiddler or, in Firefox, the Firebug addon, or point you
339 browser directly to the GZip file.
343 Consult the changelog of this script and make sure that you use the
344 latest version of TinyMCE. These two parts are pretty much tied
345 together so there is no guarantee that it will work with older versions
350 Visit the TinyMCE forum for help with the TinyMCE Gzip Compressor.
352 =head2 Changelog and Bugs
354 See the ChangeLog here : changelog.txt
356 Please report any bugs that you find to clint@traveljury.com
358 =head2 License notice
360 The perl part of this library has been written by Clinton Gormley
361 (clint@traveljury.com).
363 The javascript part has been taken from the PHP compressor available at
366 This library is under LGPL license but it uses the zlib library, which
367 is free to use in commercial applications. (Read the zlib licence).