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