Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

559 lines (396 sloc) 32.141 kB

GreenScript module

The GreenScript module help you to manage javascript and CSS dependencies and do minimizing work in the same time.

What’s new for v1.2.11

  • Upgrade lesscss-engine to 1.3.0

What’s new for v1.2.8b

  • Update rythm tags to rythm v0.9.8 version

What’s new for v1.2.8a

  • File cache now use UUID generated name also
  • Capture google closure compilation error and copy content if it happened

What’s new for v1.2.8

What’s new for v1.2.7

  • Bug fix: #30 running greenscript in a cluster on heroku
    • Special thanks to Mr.tazmaniax for his contribution! And thanks to Mr. Pere Villega and other people who reporting this issue
  • Add tag for Play Rythm template engine
    • Rythm is a static template engine using Razor like syntax. The performance is at Japid level, but much more easy to integrate with Play. Simply speaking, the way you use groovy template is the way you use rythm. Think it as unobtrusive Japid template engine with Razor like syntax.

What’s new for v1.2.6m

What’s new for v1.2.6l

What’s New for v1.2.6k

  • Fix a bug that cached content get cleared by accident

What’s New for v1.2.6j

  • Remove jcoffeescript.jar for the moment (memory leak suspect)

What’s New for v1.2.6i

  • Support LESS process for static service
    • In other words, now you can get LESS compilation even you are using static css reference in your view like . This feature could benefit those people who are using Japid or new Scala template system where GreenScript tags are not available yet
  • Support Last-Modified and E-Tag and your browser now retrieve files only once
  • Bug fix: Rhino version conflict between LessEngine and YUICompressor
  • Support both .css and .less extension for stylesheet files
  • Enable last-modified timestamp check for resource files even greenscript.minimize is turned on.
  • Bug fix: https://github.com/greenlaw110/greenscript/issues/18, e.g. support relative url path in css
  • Bug fix: https://github.com/greenlaw110/greenscript/issues/19, e.g support Play.ctxPath
  • Bug fix: https://github.com/greenlaw110/greenscript/issues/21 (found on v1.2.6h)

What’s New for v1.2.6

What’s New for v1.2.5

  • Support in-memory cache
  • New configuration item:
    • greenscript.cache.inmemory

What’s New for v1.2.4

  • add “greenscript.url.root” option which is set to “/public” by default
  • Intelligent resouce root detect. Suppose your javascript foo.js located at /public/bar/foo.js and your “greenscript.dir.root” set to “/public” as default. you can reference foo.js by either “/public/bar/foo.js” or “/bar/foo.js”, but “foo.js” is not okay, give your “greenscript.dir.js” is set to “javascripts” by default. “foo.js” will be evaluated as “/public/javascripts/foo.js”.

What’s New for v1.2.3

  • upgrade YUI compressor version from 2.4.2 to 2.4.6
  • Bug fix: 404 error while fetching cached files when change minimize/cache setting dynamically
  • Bug fix: loaded logic breaks when minimize is enabled

What’s New for v1.2.2

  • !!!Major bug fix for inline dependency declaration feature: refreshing page will cause dependency disorder
  • greenscript.conf change detection in dev mode. thanks for the contribution comes from short-at (https://github.com/shorty-at)
  • CDN resource order now kept when minimize enabled
  • Configuration controller is secure now

What’s New for v1.2d

  • configuration change (COMPATIBILITY BROKEN!): resource dir location shall NOT include resource root dir now.
    • Previously: greenscript.dir.js=/public/javascripts
    • Now: greenscript.dir.js=javascripts (suppose greenscript.dir.root=/public)
    • Previously: greenscript.dir.css=/public/stylesheets
    • Now: greenscript.dir.css=stylesheets (suppose greenscript.dir.root=/public)
  • Bug fix: https://github.com/greenlaw110/greenscript/issues#issue/11
    • greenscript now support dependency management in modules (your css/js files in modules, your greenscript.conf file in moduels)
  • Support ‘.bundle’ suffix in resource dependency configuration via greenscript.conf
  • Support inline dependency declaration (search for ‘inline dependency declaration’ in this document)

What’s New for v1.2c

  • Bug fix: dependency management breaks for complicated dependencies
  • Support reverse dependency declaration (search for “reverse dependency declaration” in this document)

What’s New for v1.2b

  • Support transparent compression. You don’t even invoke #{greenscript.css|js /} tag to get your css and js file compressed

What’s New for v1.2a

  • Bug fix: IllegalStateException thrown out when app restart (in DEV mode)

What’s New for v1.2

  • Completely rewrite.
    • GreenScript core logic detached from Play plugin project
    • Clearly defined interface and well documented code comment
    • Unit test cases for core logic
  • Circular dependence relationship detect
  • Unified javascript/css tag syntax
  • Support inline javascript/css
  • new tag options:
    • media: pass media (screen, project, all, etc) to #{greenscript.css /} to specify the css file media target
    • browser: pass to #{greenscript.css /} and #{greenscript.js /} to specify which browser it is targeted

What’s New for v1.1c

  • greenscript.compress and greenscript.cache now default to true without regarding to the Play mode
  • Unused compressed files in “gs” folder get cleaned
  • Notice in configuration html page and demo application.conf file about greenscript.compress|cache option
  • Fix bug in css.html tag: NPE encountered when trying to output without argument or “load/import”
  • Add support to CDN
  • Support reload dependency configuration at runtime

What’s New for v1.1

  • Many bug fixes
  • Completely new Plugin Configurator
  • Add Command to enable user copy module tags/templates to user app directory
  • More clear configuration settings
  • Even more simplified tag syntax
  • Support zero configuration
  • Document improvement

What’s New for v1.0

  • Bug fixes:
  • Enhancements:
    • Tag simplified: ‘sm:gsSM’ parameter no longer needed for greenscript.css and greenscript.javascript tag
    • Simplified alias for greenscript.javascript tag: greenscript.js
    • Use ‘import’ to replace ‘load’

Three steps to use GreenScript

  1. Install the module and enable it in your application.conf file
    • you know what I am talking about …
  2. Document your javascript/css dependencies in conf/greenscript.conf file
    • Check the demo’s greenscript.conf file and you will know what it is
  3. Use greenscript tag in your template files: #{greenscript.js “myjs1 myjs2 …” [options] /}
    • Yes, this part is a little bit complicated, but not that much. I am sure it won’t be difficult as #{list} tag

Step 2 and 3 are optional. The simplest form of using GreenScript is to add the following line into your application.conf file:

module.greenscript=${play.path}/modules/greenscript-xx

Immediately you have done that, your javascript file and css file will be compressed automatically.

Manual

Configure GreenScript Plugin

# The GreenScript module
module.greenscript=${play.path}/modules/greenscript-xx

File locations

This part set the javascript, css and compressed file location in the filesystem, start from your application’s root.

# Default greenscript.dir.js point to /public/javascripts
greenscript.dir.js=/public/javascripts
#
# Default dir.css point to /public/stylesheets
greenscript.dir.css=/public/stylesheets
#
# Default minimized file folder point to /public/gs
greenscript.dir.minimized=/public/gs

URL Path

This part set the url path GreenScript used to output javascript, css or compressed files, start from root url.

Usually you will not need to set this part as it will reuse the dir settings, which is comply with Play’s default folder layout and route mapping. However, if you have shortcut set in your application’s route file (as what I did in the demo app), you are encouraged to override defalt setting here:

greenscript.url.js=/js
greenscript.url.css=/css
##
# IMPORTANT: make sure the mapping does not conflict with
# the mapping of greenscript module in your route file.
# see <a href="dyna-conf">Configuration at runtime</a>
greenscript.url.minimized=/compressed

Note that js and css url is used only when greenscript.miminize set to false, in which case, GreenScript will output links refer the original javascript/css files.

greenscript.url.minimized setting is used only when greenscript.minimize set to true, in which case, GreenScript will output links refer to the compressed(minimized) files

Minimize Settings

# Enable/Disable minimize
# 	Once minimize disabled, GreenScript will output the original javascript/css
# 	files without any processing. However, the order of the files is guaranteed
#	to follow the dependency graph you have defined in "greenscript.conf" file
#
#	When minimize turned on, GreenScript will merge all javascript/css files
#	within one HTTP request into a single file. Again the merge order is
#	guaranteed to follow the dependency graph you have defined in the
#	"greenscript.conf" file
#
#	Note if you turn off minimize, the rest 2 settings (compress, cache) will
#	not function whatever the value they are
#
# By Default minimize is turned on in prod mode and turned off in dev mode
greenscript.minimize=false
#
# Enable/Disable compress
#	Once compress is enabled, GreenScript will compress the javascript/css files
#	while doing the merge operation.
#
# By default compress is turned on in prod mode and turned off in dev mode
greenscript.compress=false
#
# Enable/Disable cache
#	Once cache is turned on, GreenScript will try best to reuse the processed
#	file instead of repeat the merge/compress process.
#
# By default cache is turned on in prod mode and turned off in dev mode
greenscript.cache=false
# Enable/Disable in-memory cache
#   Once in-memory cache is turned on, GreenScript will use a memory buffer to
#   store the minimized resource instead of a temporary file. This feature could
#   be useful to those apps hosted on clouds without normal File IO, e.g. GAE
# This item is by default false
greenscript.cache.inmemory=true
#
# Enable/Disable LESS support
#   Once LESS support is enabled, GreenScript will try to compile css file
#   using LESS CSS engine before processing it. By default this option
#   is turned off
greenscript.less.enabled=false
# Enable/Disable Coffeescript support
#   Once Coffeescript support is enabled, GreenScript will try to compile
#   the .coffee file to Javascript.
#   By default this option is turned off.
greenscript.coffee.enabled=false
#
#
# Enable/Disable inline script processing
#   Once this configuration is turned on, GreenScript will try to process inline code
#   before output them. Here "process" means use less engine to compile the css code and
#   use compressor to compress both css and js code. By default the configuration is
#   turned off
greenscript.inline.process=false
#
# Set css file last-modified timestamp check frequence. default to 10 seconds
# greenscript.css.cache.check=10s
#
# Set js file last-modified timestamp check frequence. default to 10 seconds
# greenscript.js.cache.check=10s

Configure javascript/css dependencies

Javascript/css dependencies are documented in a separate configuration file named greenscript.conf, which should be put into the conf dir (the same place with your application.conf). Start from v1.2d, greenscript.conf could be put under conf dir of modules, and these module level greenscript configuration will be merged with application greenscript.conf to define the whole depenedency graph of javascript and css files located in your application and module folders. One limitation to this module level greenscript.conf support is that your javascript and css file must be put in the same directory hierarchy. For example, if you app js/css files are put into ${app.root}/public/javascripts and ${app.root}/public/stylesheets, then all your module you want to use with greenscript must also store their javascripts and css files inside ${module.root}/public/javascripts and ${module.root}/public/stylesheets.

It’s fairly straght forward to document the file dependencies. Let’s say your have four javascript files a.js, b.js, c.js and d.js, the dependency relationship is b.js depends on a.js, c.js depends on both b.js and d.js, then here is the content of your greenscript.conf file:

js.b=a
js.c=b,d

The same way applies to css file dependencies. The only difference is css dependancy rule starts with css. while javascript file rule starts with js.. Below is the content of greenscript.conf file of the demo application:

# js.default means the file get loaded always, even no other file depends on it
js.default=prototype
# Javascript Dependencies
js.datepicker=prototype-base-extensions,prototype-date-extensions
js.livevalidation=prototype
js.pMask=prototype-event-extensions
js.prototype-base-extensions=prototype
js.prototype-date-extensions=prototype
js.prototype-event-extensions=prototype
js.dumb_1=prototype
#
# CSS Dependencies
css.color=reset
css.form=color,layout
css.layout=reset
#
# Other configuration should go to application.conf

reverse dependency declaration (new in 1.2c)
bc. js.b-=a,c,d

The above line equals to three lines below:
bc. js.a=b
js.c=b
js.d=b

Using tags

Now that your have understand how to configured the plugin and file dependencies, it’s time to see how GreenScript can simplify your life of dealing with javascript/css in your play template files.

The base template: main.html

Normally you should have a main.html (you might call it “base” or other names, but that doesn’t matter) served as a base template for all other templates, and in the “

” section of the main.html you will have the following lines if you are not using GreenScript:

<link rel="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/main.css'}">
#{get 'moreStyles' /}
<script src="@{'/public/javascripts/jquery-1.4.2.min.js'}" type="text/javascript" charset="utf-8"></script>
#{get 'moreScripts' /}

And here is how it should be when you are using GreenScript:

#{greenscript.css "main", output:'all'/}
#{greenscript.js output: 'all'/}

Yes! that’s it. I know you might have some questions, don’t worry. Let me unveil the curtain.

  • Where is my “jquery-1.4.2.min.js” ?
    • When you put output: ‘all’ in #{greenscript.js} tag, it will output all unloaded js dependency files as well as the default js file you’ve defined in greenscript.conf. I am sure jquery-1.4.2.min.js will be reached by either of the 2 lookup paths, otherwise, I assume you will not need that file. For perfectionist, here is how to load the file anyway: #{greenscript.js “jquery-1.4.2.min.js”, output: true/}
  • How can I get “moreStyles” and “moreScripts”?
    • You get them automatically when you have output: true for #{greenscript.css} or output: ‘all’ for #{greenscript.js}. The assumption is you have told GreenScript that you need them in other places. I will let you know how to do that later at next section.
  • Why do you use output for css while loadAll for js?
    • loadAll is deprecated now. Both css and js use ‘output: “all”’ to output all inline declared and dependencies that has not output yet

Other templates

The differences of using GreenScript tag in other templates and in the main.html is that ususally you don’t “output” javascript or css files in your other templates, instead, you declare them (for the template usage). Here is a sample (found in ${play.path}/samples-and-tests/booking/app/views/Hotels/book.html) of how to declare javascripts and css when you don’t have GreenScript:

#{set 'moreScripts'}
    <script src="@{'/public/javascripts/jquery-ui-1.7.2.custom.min.js'}" type="text/javascript" charset="utf-8"></script>
#{/set}
#{set 'moreStyles'}
    <link rel="stylesheet" type="text/css" media="screen" href="@{'/public/ui-lightness/jquery-ui-1.7.2.custom.css'}" />
#{/set}

And see how you do with GreenScript available:

#{greenscript.js "jquery-ui-1.7.2.custom.min" /}
#{greenscript.css "/public/ui-lightness/jquery-ui-1.7.2.custom" /}

Easy, right? You might noticed that I have put the full path for the css file in this case. This is needed because the file is not in the default stylesheet file folder (configured with greenscript.dir.css, which default to /public/stylesheets).

Inline body

Greenscript Play module support inline body start from v1.2.

#{greenscript.css}
dl > dt {
    font-weight: bold;
    color: #600;
}
#{/greenscript.css}

In the above sample, the block that defines dl > dt’s style will be captured by greenscript and moved to your html page header. (Suppose you have “#{greenscript.css output:’all’}” in the header block of your main.html template. By using “output: true” parameter, the following sample will output the block in place rather than moving the enclosed body to the header:

#{greenscript.js output:true}
    var rule = ruleById('first_name');
    rule.add(Validate.Presence)
    rule = ruleById('last_name');
    rule.add(Validate.Presence)
    rule = ruleById('email');
    rule.add(Validate.Email)
    rule.add(Validate.Presence)
    $$('input.date').each(function(el){
        new Control.DatePicker(el, {icon: '/public/images/calendar.png', locale: 'en_iso8601'});
    });
#{/greenscript.js}

support LESS syntax in css files

Start from v1.2.6 GreenScript support LESS syntax in css files. To enable LESS support, you need to set the following configuration
to true:

greenscript.less.enabled=true

Note, all css files must be suffixed with “.css”, “.less” files will NOT be recognized by Greenscript!

Inline css code with LESS support

Start from v1.2.6 GreenScript Play module support LESS syntax in inline css code.

#{greenscript.css}
#{greenscript.@import 'lib/color' /}
#body {
  color: @fgColor;
  background-color: @bgColor;
}
#{/greenscript.css}

Note, this feature should be used with cautious. By embedding less code in view you will have less engine to compile the view each time. While force less code sit inside static css file only you just need to invoke less engine for one time and the rest requests will fetch the compiled version from cache. This is why we have “greenscript.inline.process” configuration defined.

Inline Dependency Declaration

There is a long time complaint that greenscript does not guarantee the output sequence of resource (js/css) files match the sequence of declaring those files in tags. For example, #{greenscript.js ‘a b c’/} does not necessarily output or marge javascript ‘a.js’, ‘b.js’ and ‘c.js’ in a sequence that a.js followed by b.js and then c.js. This is because greenscript output is driven by dependencies (which is defined in greenscript.conf), rather than the sequence declared in tag. Actually greenscript cannot and shouldn’t follow the sequence declared in tag at all. The reason is
1. the sequence of tag declaration might conflict with dependencies declared in greenscript.conf
2. it is hard to tell the sequence of tag declaration when the developer declare resources in multiple templates with inheritance relationships

Now (start from v1.2d) greenscript support inline dependency declaration in tags, which basically remove the inconvenience that simple resource file dependencies are also require developer to provide a greenscript.conf file:

#{greenscript.js 'myapp < mylib < jquery-1.5.min'/}

The above javascript declaration also setup the dependencies among the declared javascript resources: myapp.js relies on mylib.js which in turn relies on jquery-1.5.min. Therefore you no longer need a greenscript.conf file to define the dependencies among myapp, mylib and jquery-1.5.min javascript files.

The limitation of inline dependency declaration is you can’t use it across multiple template files. Say you have a javascript A declared in main.html and then you have another javascript B declared in index.html, you can’t use inline dependency declaration to declare the dependency relationship between A and B unless you declare them all in index.html: #{greenscript ‘A > B’/}

Reference

A > B means B relies on A
A < B means A relies on B

Media and Browser

GreenScript support media and browser options start from v1.2. Issue tag “#{greenscript.css 'print.css', media: 'printer'}” to declare a css resource target to “printer” media. Later when you output all css files by “#{greenscript.css output:'all'}”, one line will be output as:

<link rel="stylesheet" type="text/css" media="printer" href="/compressed/print.css">

Declare resource specific to a browser in the following way:

#{greenscript.css 'ie', browser: 'lt IE 8'/}

The corresponding output is:


<!--[if lt IE 8]>
<link rel="stylesheet" type="text/css" media="all" href="/compressed/ie.css">
<![endif]-->

Configuration at runtime

This beautiful feature enable app developer to turn on/off minimizing dynamically and could be very helpful when you need to debug your javascript/css. In order to use the feature, you will need to add an entry in your route file to map a url request to the controllers.greenscript.Configurator actions, for example:

# Enable GreenScript dynamic configuration
# IMPORTANT: make sure this routine map be different from your
# staticDir map to compressed file folder
GET /gsconf module:greenscript

Once you have done with that, you can reach the configuration page by typing http://localhost:9000/gsconf in the address bar of your favorite browser. The configuration is designed to be self guided and you won’t lost yourself there. Please be noted that runtime configuration will not be flushed to your configuration file. When you restart your app all the configurations you’ve made during last session are lost. Meaning if you want to change a configuration permanently, you must update your application.conf file. See Configuration section for detail.

You can also force GreenScript to reload the dependency configuration from “greenscript.conf” file if you have changed it. Just go to “css/js dependencies” tab and click “reload”. This feature is very friendly to developer, especially in the early stage of javascript involved development.

About Security
p. There is no integrated security to access the configuration page. And here is my 2 cents on how to secury your GreenScript dynamic configuration access:

  • Option 1: Remove the url mapping entry in your route file in a prod environment
  • Option 2: If you are a real hack and reject any manual operations, you will probably implement your own controller extends (or @With) controllers.greenscript.Configurator, and then add security to your controller. You will need to copy the configurator templates to your views folder. Don’t worry, GreenScript provides command to help you with that. I will get there now.

Module command

I’ve just told you that you can use command to copy the greenscript Configurator.configure.html template file to your app folder. Here is how to do it. First make sure you have enabled greenscript in your application.conf file. And then go to the console, enter your app root dir, and type:

play greenscript:cp -t MyGSConfigurator

The template file will be ready in {your-app-root}/app/views/MyGSConfigurator folder. Obviously your controller should be named MyGSConfigurator. It probably should looks like:

 package controllers;

import play.mvc.*;

@With({Secure.class, controllers.greenscript.Configurator.class})
@Secure(role='developer,admin')
public class MyGSConfigurator extends Controller {
}

Keep it secret!

Okay, how do you feel about this Plugin? Still not satisfied because you don’t like to type 11 charaters for tag name each time? Well I have a secret weapon to alleviate your pain with that: Once you have enabled greenscript in your conf/application.conf file, go to console, enter your app root, and type:

play greenscript:cp -a gs

Now guess what happened? You are right, it copied the tags from module folder to your app folder: your-app-root/app/views/tag/gs. And now you can use tags in short version: #{gs.js “js1 js2 …” /} and #{gs.css “css1 …” /}. What? you are still not satisfied? how come? it’s already shorter than play’s #{script} tag! Okay, here is my nuclear weapon:

play greenscript:cp -a .
...
#{js output: 'all'/}
#{js "js1 js2 ..." /}
..
#{css "css1 css2" /}

How do you expect anything more simpler than this?

Zero configuration

GreenScript plugin now support zero configuration. It means besides enabling it in your application.conf, you don’t need to do any configuration to use it, you don’t even need to create “greenscript.conf” file in your conf dir. But what do you get if you don’t do any configuration? Well basically you can still benefit from GreenScript with zero configuration:

  • The tags. You are free to utilize all the knowledges you’ve learned from Using Tags section.
  • Minimize/compress. You can also benefit from minimizing/compressing/cache capability of GreenScript.
  • Dynamic configuration. You can also use the dynamic configuration controller.

So what do you lost without any configuration?

  • Dependency management. Without dependencies infomation defined in greenscript.conf file, you are on your own to take care of js/css file dependencies. When you are declare a javascript or css file in a template, you should also make sure all its dependencies are explicitly declared before that scripts “IN PLACE”! If you failed to do that, you might get a lot of script/css errors in your final rendered html page.
  • Dir/URL path bound to play’s default. With zero configration, you need to make sure your dir structure (the public) and route mapping of public dir strictly follow Play’s convention. Otherwise GreenScript won’t be able to locate the javascript/css files.

As an example to demonstrate zero configuration, I put the ${play.path}/samples-and-tests/booking sample in the samples-and-tests dir of greenscript, makes the minimum changes to the templates and application.conf files.

Transparent Compression

GreenScript support Transparent Compression start from v1.2b. With Transparent Compression, even you don’t use greenscript tag, your js file and css file will automatically get compressed even without your attention (in PROD mode).

In conclusion, GreenScript is a flexible and powerful javascript/css management tool for your Play applicaiton development. You can use it in simple way (zero configuration) or in a way sophsicated enough to manage your complicated javascript and css dependencies.

CDN Support

Greenscript support CDN start from version 1.1a.

Configure CDN dependencies

# Note you must escape ':' if it is in the 'key' part, no need to escape if
# it's in the 'value' part. This is due to the java.util.Properties treat ':'
# as separator between key and value
js.http\://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.3/scriptaculous.js=http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js

Load CDN items in tags

#{greenscript.js 'http://www.google.com/jsapi' /}

FAQ

I found there is no javascript and css links at all in my html file rendered out!!

Make sure you have add the following lines in your main.html (or in any other name) template:

#{greenscript.css "css files separated by blank", output:'all'/}
#{greenscript.js output:'all'/}

Do I need to surround #{greenscript } tag with #{set ’moreStyles’} in my other templates?

No, you just use #{greenscript.css ‘…’ /} to declare your css files. With greenscript, you can say ‘byebye’ to ‘moreStyles’ and ‘moreScripts’.

How to use GreenScript? Is it hard to configure?

You can use GreenScript with zero configuration. However, it’s suggested to create “greenscript.conf” file to describe your javascript and css file dependancies. You will love this feature because you just need to declare explicitly used javascript/css files in your templates, leave the dependencies to GreenScript.

I want to debug javascript, can GreenScript output uncompressed version of javascript/css files?

Yes, put “greenscript.minimize=false” in your application.conf file. Actually the setting is turned off by default when you are running app in “dev” mode. An nice feature you can use is dynamic configuration which enable you turn on/off minimizing/compressing without restart your app. See Configuration at runtime section for detail

Why don’t you use GreenScript in the dynamic configuration feature?

Well, I have no idea how you will configure the dir/url path settings, so I have to hard code my javascript/css links in my template. Fortunately it’s not a big work for a single page web app ;-)

Jump to Line
Something went wrong with that request. Please try again.