/** * Copyright 2014 55 Minutes (http://www.55minutes.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package fiftyfive.wicket.js; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.List; import fiftyfive.wicket.js.locator.DefaultJavaScriptDependencyLocator; import fiftyfive.wicket.js.locator.JavaScriptDependencyLocator; import fiftyfive.wicket.js.locator.SearchLocation; import fiftyfive.wicket.js.locator.SprocketsParser; import fiftyfive.wicket.js.locator.SprocketsParserImplV4; import org.apache.wicket.Application; import org.apache.wicket.MetaDataKey; import org.apache.wicket.request.resource.PackageResourceReference; import org.apache.wicket.request.resource.ResourceReference; import org.apache.wicket.util.lang.Args; import org.apache.wicket.util.time.Duration; /** * Settings that affect how JavaScript dependencies are located. To obtain an * instance, use the static {@link #get get()} method. * <p> * Most applications using out-of-the-box JavaScript widgets will be served * well by the default settings. Before changing the settings, review * the <a href="package-summary.html#dependency-resolution">dependency * resolution guide</a> to get a complete understanding of how * fiftyfive-wicket-js loads JavaScript files and determines their * dependencies. * <p> * Then consider these scenarios: * <ol> * <li><b>If you want to disable dependency resolution entirely, * call {@link #setSprocketsParser setSprocketsParser(null)}.</b></li> * <li><b>If your application already has a mechanism for including jQuery * and/or jQuery UI in the <head></b> (e.g. via your base page), * then you should take care that fiftyfive-wicket-js is not including its * own copies of these libraries. In this scenario, call * {@link #setJQueryResource setJQueryResource()} and * {@link #setJQueryUIResource setJQueryUIResource()} with references to * the files that your application is already using (or {@code null}, * if you don't want fiftyfive-wicket-js to manage these resources at all). * </li> * <li><b>If you have your own library of custom JavaScript files</b>, * consider placing those files inside your classpath, and then use * {@link #addLibraryPath addLibraryPath()} to make fiftyfive-wicket-js * aware of them. Then you can use the {@code //=require} dependency syntax * or the single argument constructor for * {@link JavaScriptDependency JavaScriptDependency}, and those files will * be found automatically.</li> * <li><b>If you'd like to change the jQuery UI CSS theme</b>, call * {@link #setJQueryUICSSResource setJQueryUICSSResource()} with a * reference to the desired CSS file, or {@code null} if you don't want * fiftyfive-wicket-js to manage this for you.</li> * </ol> * * @since 2.0 */ public class JavaScriptDependencySettings { private static final MetaDataKey<JavaScriptDependencySettings> SETTINGS_KEY = new MetaDataKey<JavaScriptDependencySettings>() {}; private Application app; private List<SearchLocation> locations; private ResourceReference jQueryResource; private ResourceReference jQueryUIResource; private ResourceReference jQueryUICSSResource; private Duration traversalCacheDuration; private String encoding; private JavaScriptDependencyLocator locator; private SprocketsParser sprocketsParser; /** * Returns the JavaScriptDependencySettings associated with the current * Wicket Application, creating a new default settings object if one does * not yet exist. This method can only be called within a Wicket thread. */ public static JavaScriptDependencySettings get() { Application app = Application.get(); if(null == app) { throw new IllegalStateException( "No thread-local Wicket Application object was found. " + "JavaScriptDependencySettings.get() can only be called " + "within a Wicket request." ); } JavaScriptDependencySettings settings = app.getMetaData(SETTINGS_KEY); if(null == settings) { settings = new JavaScriptDependencySettings(app); app.setMetaData(SETTINGS_KEY, settings); } return settings; } /** * Constructs a settings object for the given application with a reasonable * set of defaults. */ protected JavaScriptDependencySettings(Application app) { super(); this.app = app; this.locator = new DefaultJavaScriptDependencyLocator(); this.sprocketsParser = new SprocketsParserImplV4(); this.locations = new ArrayList<SearchLocation>(); Class<?> c = JavaScriptDependencySettings.class; addLibraryPath(c, ""); addLibraryPath(c, "lib"); addLibraryPath(c, "lib/fiftyfive-utils"); this.jQueryResource = new PackageResourceReference( JavaScriptDependencySettings.class, "lib/jquery.js" ); this.jQueryUIResource = new PackageResourceReference( JavaScriptDependencySettings.class, "lib/jquery-ui.js" ); this.jQueryUICSSResource = new PackageResourceReference( JavaScriptDependencySettings.class, "lib/jquery-ui/themes/redmond/jquery-ui.redmond.css" ); } /** * Returns the JavaScriptDependencyLocator for this application. */ public JavaScriptDependencyLocator getLocator() { return this.locator; } /** * Sets the JavaScriptDependencyLocator that should be used for this * application. By default this is * {@link DefaultJavaScriptDependencyLocator}. * * @return {@code this} to allow chaining */ public JavaScriptDependencySettings setLocator(JavaScriptDependencyLocator loc) { Args.notNull(loc, "loc"); this.locator = loc; return this; } /** * Returns the list of classpath locations that will be used to find * JavaScript libraries. */ public List<SearchLocation> getLibraryPaths() { return Collections.unmodifiableList(this.locations); } /** * Adds a classpath location to be used for locating JavaScript libraries. * A default list of locations from the fiftyfive-wicket-js JAR contains * jQuery, jQuery UI, cookies, fiftyfive-utils, jquery-scrollto and * strftime. * Call this method if you wish to add more libraries to this list, or if * you want your versions of the previously mentioned libraries to take * precedence. The paths that you add will be consulted before the * defaults. * <p> * Library paths are searched whenever you use the {@code //= require} syntax * for JavaScript dependencies, like this: * <pre class="example"> * //= require libraryname</pre> * <p> * Or when you use * {@link JavaScriptDependency#JavaScriptDependency(String)} or * {@link MergedJavaScriptBuilder#addLibrary(String)}. * * @return {@code this} to allow chaining */ public JavaScriptDependencySettings addLibraryPath(Class<?> cls, String path) { this.locations.add(0, new SearchLocation(cls, path)); return this; } /** * Returns the ResourceReference for the jQuery JavaScript file. */ public ResourceReference getJQueryResource() { return this.jQueryResource; } /** * Sets the ResourceReference of the jQuery JavaScript file that should * be used whenever the dependency locator determines that jQuery is * needed. Pass {@code null} if you do not want the framework to include * jQuery automatically. * By default this refers to a copy of jQuery bundled in the * fiftyfive-wicket-js JAR. * * @return {@code this} to allow chaining */ public JavaScriptDependencySettings setJQueryResource(ResourceReference r) { this.jQueryResource = r; return this; } /** * Returns the ResourceReference for the jQuery UI JavaScript file. */ public ResourceReference getJQueryUIResource() { return this.jQueryUIResource; } /** * Sets the ResourceReference of the jQuery UI JavaScript file that should * be used whenever the dependency locator determines that jQuery UI is * needed. Pass {@code null} if you do not want the framework to include * jQuery UI automatically. * By default this refers to a copy of jQuery UI bundled in the * fiftyfive-wicket-js JAR. * * @return {@code this} to allow chaining */ public JavaScriptDependencySettings setJQueryUIResource(ResourceReference r) { this.jQueryUIResource = r; return this; } /** * Returns the ResourceReference for the jQuery UI CSS theme file. */ public ResourceReference getJQueryUICSSResource() { return this.jQueryUICSSResource; } /** * Sets the ResourceReference of the jQuery UI CSS theme file that should * be used whenever the dependency locator determines that jQuery UI is * needed. Pass {@code null} if you do not want the framework to include * jQuery UI CSS automatically. * By default this refers to a copy of jQuery UI "redmond" theme bundled * in the fiftyfive-wicket-js JAR. * * @return {@code this} to allow chaining */ public JavaScriptDependencySettings setJQueryUICSSResource(ResourceReference r) { this.jQueryUICSSResource = r; return this; } /** * Returns the strategy that will be used to parse Sprockets {@code //= require} * directives. The default is {@link SprocketsParserImplV4}. * @since 4.0 */ public SprocketsParser getSprocketsParser() { return this.sprocketsParser; } /** * Sets the strategy that will be used to parse Sprockets {@code //= require} * directives. The default strategy changed in fiftyfive-wicket-js 4.0. Starting * with 4.0 the default is {@link SprocketsParserImplV4}. If you would like to * restore the Sprockets behavior of earlier versions, pass an instance of * {@link fiftyfive.wicket.js.locator.SprocketsParserImplV3 SprocketsParserImplV3}. * <p> * The two versions differ in how they resolve paths specified in the * {@code //= require} directive. Refer to the documentation for each implementation * for further details. * <p> * Pass {@code null} to disable Sprockets parsing altogether. * * @return {@code this} to allow chaining * @since 4.0 */ public JavaScriptDependencySettings setSprocketsParser(SprocketsParser p) { this.sprocketsParser = p; return this; } /** * Returns the duration that JavaScript dependency traversal results * (i.e. the tree of dependencies as determined by parsing sprockets * directives) are allowed to be stored in cache. */ public Duration getTraversalCacheDuration() { // Interpret null based on application mode if(null == this.traversalCacheDuration) { if(this.app.usesDeploymentConfig()) { // Cache indefinitely return Duration.MAXIMUM; } // Disable cache return Duration.NONE; } return this.traversalCacheDuration; } /** * Sets the duration that JavaScript dependency traversal results * (i.e. the tree of dependencies as determined by parsing sprockets * directives) are allowed to be stored in cache. * <p> * If this is set to {@code null} (the default), at runtime this will be * interpreted as a value of zero (cache disabled) if the application is * in development mode, and will be interpreted as the maximum duration * (effectively indefinite) if the application is in deployment mode. * * @return {@code this} to allow chaining */ public JavaScriptDependencySettings setTraversalCacheDuration(Duration d) { this.traversalCacheDuration = d; return this; } /** * Returns the character encoding that will be used when parsing * JavaScript files. */ public String getEncoding() { if(null == this.encoding) { String enc = this.app.getMarkupSettings().getDefaultMarkupEncoding(); if(null == enc) { enc = Charset.defaultCharset().name(); } return enc; } return this.encoding; } /** * Sets the character encoding that will be used when parsing * JavaScript files. If this is set to {@code null} (the default), then * Wicket's markup encoding setting will be used. * * @return {@code this} to allow chaining * @see org.apache.wicket.settings.IMarkupSettings#getDefaultMarkupEncoding() */ public JavaScriptDependencySettings setEncoding(String encoding) { this.encoding = encoding; return this; } }