Community contributed extensions

press

The press module is a JavaScript and CSS minimizer that is designed to be transparent to the application developer:
Simply replace <script> tags with #{press.script} tags, and <link rel="stylesheet"> tags with #{press.stylesheet} tags.

Press now also supports Less CSS.

Enable press

Run the following command to install the press module in your copy of Play, and note the version of the module that is installed.

play install press

Include the following line in your application’s conf/application.conf file to load the press module. Be sure to use the appropriate version number:

module.press=${play.path}/modules/press-1.0

By default, press does not minimize JavaScript and CSS files when Play is in dev mode. Add the following line to conf/application.conf in order to see the compressed output in dev:

press.enabled=true

Add the following line to conf/routes to import the press routes. This simply makes the urls used by the press module more uniform:

*  /  module:press

Use press to aggregate and compress files

Replace <script> with #{press.script}

For example, your template includes the following JavaScript files:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
<script src="/public/javascripts/header.login.js" type="text/javascript"><script>
<script src="/public/javascripts/main.js" type="text/javascript"></script>
<script src="/public/javascripts/library.min.js" type="text/javascript"></script>

You would change the template to look like this:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
#{press.script 'header.login.js' /}
#{press.script 'main.js' /}
#{press.script src:'library.min.js', compress:false /}
#{press.compressed-script /}

The #{press.compressed-script /} tag outputs a <script> tag whose source is the compressed output of the other files.

Note:

Replace <link rel="stylesheet"> with #{press.stylesheet}

For example, your template includes the following CSS files:

<link href="/public/stylesheets/styles.css" rel="stylesheet"></link>
<link href="/public/stylesheets/header.login.css" rel="stylesheet"></link>

You would change the template to look like this:

#{press.stylesheet 'header.login.css' /}
#{press.stylesheet 'main.css' /}
#{press.compressed-stylesheet /}

Sample output

The above example will output something that looks like this:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript" language="javascript" charset="utf-8"></script>
<!-- press-js: header.login.js -->
<!-- press-js: main.js -->
<!-- press-js: libary.min.js -->
<script src="/press/js/sNJSWMCDDFAekXYWryWgigJJ.js" type="text/javascript" language="javascript" charset="utf-8"></script>
<!-- press-css: header.login.css -->
<!-- press-css: main.css -->
<link href="/press/css/mDcFcAqEAhDvWvFVBfCOiQJJ.css" rel="stylesheet" type="text/css" charset="utf-8" ></link>

All JavaScript files are compressed into a single JS file and all CSS files are compressed into a single CSS file. press outputs HTML comments indicating the order in which each JavaScript/CSS file was added to compression

Use press to compress files individually

Instead of compressing files in a group, you can instead compress files individually (without aggregating them). To compress files individually use the #{press.single-script} and #{press.single-stylesheet} tags.

#{press.single-script 'widget.js' /}
#{press.single-stylesheet 'header.login.css' /}
#{press.single-stylesheet 'widget.css' /}

The above example will output something that looks like this:

<script src="/public/javascripts/press/widget.min.js" type="text/javascript" language="javascript" charset="utf-8"></script>
<link href="/public/stylesheets/press/header.login.min.css" rel="stylesheet" type="text/css" charset="utf-8" ></link>
<link href="/public/stylesheets/press/widget.min.css" rel="stylesheet" type="text/css" charset="utf-8" ></link>

Wildcard matching

When managing a lot of CSS or JS files in a project, instead of manually maintaining a press tag in a template for each file, you can include all files from a specific path by wildcard:

#{press.script '*.js' /}
#{press.script 'some/path/*.js' /}

Any files matching the given extension are included, in ascending alphabetical order. Note, however, that partial glob patterns such as “path/prefix-*.js” aren’t currently supported. The search can be made recursive by adding another asterisk:

#{press.script '**.js' /}

The above will include all .js files from under your JavaScripts path (“/public/javascripts” for example).

Tips

Server Caching

When a compressed file is generated for a given set of input files, it is stored on disk in a configurable location. The next time the same set of files in the same order is requested, the file is retrieved from the cache. This is server side caching.

press has three different caching mechanisms, of which you can choose one.

Client Side Caching

With the Change caching strategy, press will generate the compressed file name from the last modified timestamp of each file added to compression. Cache control headers are sent when the file is served so that the browser will cache the file. If any of the component files changes the compressed file name will also change, and the browser will request the new file.

With the Always or Never caching strategies, the last modified timestamp is not part of the file name, and no cache-enabling header is sent.

In-Memory storage

To improve performance, or to use press on systems such as Google App Engine that do not support writes to the file system, press can be configured to store all compressed files in memory using Play’s standard caching mechanism, instead of on the file system. See Configuration below.

Configuration

Many configuration options are different between dev and production. All of them can be overridden. For more information on how to override a Play configuration option for a particular environment, see Managing application.conf in several environments

press.enabled

Whether or not press is enabled.

press.enabled=true
Each #{press.script} and #{press.stylesheet} tag will output an HTML comment
The #{press.compressed-script} and #{press.compressed-stylesheet} tags will output a <script> or <link rel="stylesheet"> tag with its src attribute pointing to the compressed source.

press.enabled=false
Each #{press.script} and #{press.stylesheet} tag will output a corresponding <script> or <link rel="stylesheet"> tag with the original, uncompressed source.
The #{press.compressed-script} and #{press.compressed-stylesheet} tag will output nothing

By default, when play is in dev mode press is disabled, and in production it is enabled

press.cache

The caching strategy to use. See Caching.

press.cache=Change
The compressed file will be regenerated when the last modified date of one of the source js or css files is detected to have changed. This mode is recommended as the appropriate cache control headers are sent, allowing the browser to cache the file.

press.cache=Always
The server cache will always be used, regardless of whether the component files have changed. The server cache will be cleared when the server is restarted. Client side caching is disabled.

press.cache=Never
The cache will not be used: the js and css files will be compressed on every request, and client side caching is disabled.

By default, the caching strategy is Change.

press.cache.clearEnabled

Indicates whether the action to clear the cache from the web is enabled in production.
Press exposes an action to clear the compressed js and css cache at /press/clear

By default, when play is in dev mode the action is available and in production it is disabled.
press.cache.clearEnabled=true

press.inMemoryStorage

Indicates whether to store compressed files in memory or in the file system. See In-Memory Storage.

By default files are stored on the file system
press.inMemoryStorage=false

press.p3pHeader

The P3P header to output. eg
press.p3pHeader=CP="IDC DSP CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT"

If missing or empty, no P3P header is output.

By default the P3P header is not output.
press.p3pHeader=

press.key.lifetime

The amount of time to keep the compression key, in play Time duration format (see play.libs.Time.parseDuration)

When the #{press.compressed-script} or #{press.compressed-stylesheet} tag is output, a temporary key is generated and used as part of the file name. The browser then requests the file and the server responds with the compressed javascript. So the key must last as long as the time between when the browser receives the HTML and when it makes the request for the JS. The default is 2 minutes
press.key.lifetime=2mn

press.compression.maxTimeMillis

The maximum amount of time in milli-seconds that compression is allowed to take before a timeout exception is thrown.
press.compression.maxTimeMillis=60000

press.js.sourceDir

The source directory for javascript files, relative to the application root
press.js.sourceDir=/public/javascripts/

press.css.sourceDir

The source directory for css files, relative to the application root
press.css.sourceDir=/public/stylesheets/

press.js.outputDir

The output directory where compressed javascript files will be written to, relative to the application root. Note: This directory will be created if it doesn’t exist.
press.js.outputDir=/public/javascripts/press/

press.css.outputDir

The output directory where compressed css files will be written to, relative to the application root. Note: This directory will be created if it doesn’t exist.
press.css.outputDir=/public/stylesheets/press/

press.htmlCompatible

By default, the output produced by press is compatible with XHTML. This means that <link> tags are closed. If press.htmlCompatible is true, the output will be compatible with HTML, meaning that <link> tags will not be closed.
press.htmlCompatible=false

Options for the YUI compressor

See http://developer.yahoo.com/yui/compressor for details

press.yui.css.lineBreak=-1
press.yui.js.lineBreak=-1
press.yui.js.munge=true
press.yui.js.warn=false
press.yui.js.preserveAllSemiColons=false
press.yui.js.preserveStringLiterals=false

How press works

press uses YUI Compressor to perform JavaScript and CSS minimization.

Each #{press.script} tag is replaced with a corresponding HTML comment, eg <!-- press-js: main.js -->. This is necessary in order to indicate the order in which the files are declared, required to generate the compressed output (explained below).

The #{press.compressed-script} tag is replaced with a <script> tag containing a key derived from the page URL, eg

<script src="/press/js/sNJSWMCDDFAekXYWryWgigJJ.js" type="text/javascript" language="javascript" charset="utf-8"></script>

When the page is ready to be sent to the browser, press scans the output for comments of the form <!-- press-js: main.js --> and creates a list of files that will be compressed, that is associated with the key.

When the browser makes a request for /press/js/sNJSWMCDDFAekXYWryWgigJJ.js, press extracts the key from the file path and uses it to retrieve the list of files. If there is already a compressed file containing those files in that order in the cache, press returns that file to the browser. Otherwise it generates the compressed file on the fly and saves it to the cache.

The process is the same for CSS files.

Gotchas

Relative image urls will not work in a compressed CSS file because the location of the file that is output by press is not the same as the original file. Use absolute urls.

Some utilities (such as Aloha text editor) will attempt to build a path using the dynamically generated url paths (eg /press/js/sNJSWMCDDFAekXYWryWgigJJ.js). In order to get around this problem you can add the following at the bottom of your routes file, after including the press routes:

GET      /press/js/         staticDir:public/javascripts
GET      /press/css/         staticDir:public/stylesheets

Don’t use press from within a 404.html or 500.html template. When invoking an error template Play changes the request object causing press to fail.

Thanks

The following people have contributed to the development of this plugin:

Thanks!

Questions

If anything in the documentation is not clear, if you find a bug, or if you have questions, please use the play google group and include the word “press” in the subject line:
http://groups.google.com/group/play-framework