001// Copyright 2012 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007// http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry5.services.javascript;
016
017import org.apache.tapestry5.ioc.Resource;
018
019import java.util.Arrays;
020import java.util.List;
021
022/**
023 * Used to define a <a href="http://requirejs.org/docs/api.html#config-shim">module shim</a>, used to adapt non-AMD JavaScript libraries
024 * to operate like proper modules.  This information is used to build up a list of dependencies for the contributed JavaScript module,
025 * and to identify the resource to be streamed to the client.
026 * <p/>
027 * Instances of this class are contributed to the {@link ModuleManager} service;  the contribution key is the module name
028 * (typically, a single word).
029 * <p/>
030 * In some cases, an instance may be created and contributed to override a default module; if the module has no dependencies,
031 * exports, or initExpression (that is, if it is a proper AMD module, where such dependencies are provided inside
032 * the module itself), then no client-side shim configuration will be written for the module, but requests for the
033 * module will be satisfied by the resource.'
034 *
035 * @since 5.4
036 */
037public final class JavaScriptModuleConfiguration
038{
039    /**
040     * The resource for this shim module.
041     */
042    public final Resource resource;
043
044    /**
045     * The names of other shim modules that should be loaded before this shim module.
046     */
047    private List<String> dependencies;
048
049    /**
050     * Optional (but desirable) value exported by the shim module.
051     */
052    private String exports;
053
054    private String initExpression;
055
056    private boolean needsConfiguration;
057
058    public JavaScriptModuleConfiguration(Resource resource)
059    {
060        assert resource != null;
061
062        this.resource = resource;
063    }
064
065    /**
066     * A list of other module names the shim depends on.
067     *
068     * @param moduleNames
069     * @return this JavaScriptModuleConfiguration for further configuration
070     */
071    public JavaScriptModuleConfiguration dependsOn(String... moduleNames)
072    {
073        assert moduleNames.length > 0;
074
075        dependencies = Arrays.asList(moduleNames);
076
077        needsConfiguration = true;
078
079        return this;
080    }
081
082    public List<String> getDependencies()
083    {
084        return dependencies;
085    }
086
087    /**
088     * The name of a global variable exported by the module. This will be the value injected into
089     * modules that depend on the shim.
090     *
091     * @return this JavaScriptModuleConfiguration for further configuration
092     */
093    public JavaScriptModuleConfiguration exports(String exports)
094    {
095        assert exports != null;
096
097        this.exports = exports;
098
099        needsConfiguration = true;
100
101        return this;
102    }
103
104    public String getExports()
105    {
106        return exports;
107    }
108
109    /**
110     * Used as an alternative to {@linkplain #exports(String)}, this allows a short expression to be specified; the
111     * expression is used to initialize, clean up, and (usually) return the module's export value. For Underscore, this
112     * would be "_.noConflict()".  If the expression returns null, then the exports value is used.
113     * <p/>
114     * In RequireJS 2.1.1 (the version shipped with Tapestry, currently), an init function is not invoked unless
115     * the shim also defines an exports. See <a href="https://github.com/jrburke/requirejs/issues/517">RequireJS issue 517</a>.
116     * At this time, you should specify {@link #exports} even if you provide a {@link #initializeWith(String)}}.
117     *
118     * @param expression
119     *         initialization expression
120     * @return this JavaScriptModuleConfiguration, for further configuration
121     */
122    public JavaScriptModuleConfiguration initializeWith(String expression)
123    {
124        assert expression != null;
125
126        this.initExpression = expression;
127
128        needsConfiguration = true;
129
130        return this;
131    }
132
133    public String getInitExpression()
134    {
135        return initExpression;
136    }
137
138    /**
139     * Returns true if the module contains any additional configuration beyond its {@link Resource}.
140     */
141    public boolean getNeedsConfiguration()
142    {
143        return needsConfiguration;
144    }
145}