001// Copyright 2010-2013 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.Asset;
018import org.apache.tapestry5.ComponentResources;
019import org.apache.tapestry5.FieldFocusPriority;
020import org.apache.tapestry5.SymbolConstants;
021import org.apache.tapestry5.annotations.Environmental;
022import org.apache.tapestry5.internal.services.DocumentLinker;
023import org.apache.tapestry5.json.JSONArray;
024import org.apache.tapestry5.json.JSONObject;
025import org.apache.tapestry5.services.EnvironmentalShadowBuilder;
026
027/**
028 * The JavaScriptSupport environmental is very stateful, accumulating JavaScript stacks, libraries and initialization
029 * code until the end of the main page render; it then updates the rendered DOM (adding <script> tags to the
030 * <head> and <body>) before the document is streamed to the client.
031 * <p/>
032 * JavaScriptSupport is normally accessed within a component by using the {@link Environmental} annotation on a
033 * component field. In addition, JavaScriptSupport may also be accessed as a service (the service
034 * {@linkplain EnvironmentalShadowBuilder internally delegates to the current environmental instance}), which is useful
035 * for service-layer objects.
036 * <p/>
037 * The term "import" is used on many methods to indicate that the indicated resource (stack, library or stylesheet) will
038 * only be added to the final cocument once, even when there are repeated calls.
039 * <p/>
040 * The name is slightly a misnomer, since there's a side-line of {@linkplain #importStylesheet(StylesheetLink)} as well.
041 * <p/>
042 * JavaScriptSupport works equally well inside an Ajax request that produces a JSON-formatted partial page update response.
043 *
044 * @see org.apache.tapestry5.internal.services.DocumentLinker
045 * @since 5.2.0
046 */
047public interface JavaScriptSupport
048{
049    /**
050     * Allocates a unique id based on the component's id. In some cases, the return value will not precisely match the
051     * input value (an underscore and a unique index value may be appended).
052     *
053     * @param id
054     *         the component id from which a unique id will be generated
055     * @return a unique id for this rendering of the page
056     * @see org.apache.tapestry5.ioc.util.IdAllocator
057     */
058    String allocateClientId(String id);
059
060    /**
061     * As with {@link #allocateClientId(String)} but uses the id of the component extracted from the resources.
062     *
063     * @param resources
064     *         of the component which requires an id
065     * @return a unique id for this rendering of the page
066     */
067    String allocateClientId(ComponentResources resources);
068
069    /**
070     * Adds initialization script at {@link InitializationPriority#NORMAL} priority.
071     *
072     * @param format
073     *         format string (as per {@link String#format(String, Object...)}
074     * @param arguments
075     *         arguments referenced by format specifiers
076     * @deprecated Deprecated in 5.4; refactor to use {@linkplain #require(String) JavaScript modules} instead
077     */
078    void addScript(String format, Object... arguments);
079
080    /**
081     * Adds initialization script at the specified priority.
082     *
083     * @param priority
084     *         priority to use when adding the script
085     * @param format
086     *         format string (as per {@link String#format(String, Object...)}
087     * @param arguments
088     *         arguments referenced by format specifiers
089     * @deprecated Deprecated in 5.4; refactor to use {@linkplain #require(String) JavaScript modules} instead
090     */
091    void addScript(InitializationPriority priority, String format, Object... arguments);
092
093    /**
094     * Adds a call to a client-side function inside the Tapestry.Initializer namespace. Calls to this
095     * method are aggregated into a call to the Tapestry.init() function. Initialization occurs at
096     * {@link InitializationPriority#NORMAL} priority.
097     *
098     * @param functionName
099     *         name of client-side function (within Tapestry.Initializer namespace) to execute
100     * @param parameter
101     *         object to pass to the client-side function
102     * @deprecated Deprecated in 5.4; refactor to use {@linkplain #require(String) JavaScript modules} instead
103     */
104    void addInitializerCall(String functionName, JSONObject parameter);
105
106    /**
107     * Adds a call to a client-side function inside the Tapestry.Initializer namespace. Calls to this
108     * method are aggregated into a call to the Tapestry.init() function. Initialization occurs at
109     * {@link InitializationPriority#NORMAL} priority.
110     *
111     * @param functionName
112     *         name of client-side function (within Tapestry.Initializer namespace) to execute
113     * @param parameter
114     *         array of parameters to pass to the client-side function
115     * @since 5.3
116     * @deprecated Deprecated in 5.4; refactor to use {@linkplain #require(String) JavaScript modules} instead
117     */
118    void addInitializerCall(String functionName, JSONArray parameter);
119
120    /**
121     * Adds a call to a client-side function inside the Tapestry.Initializer namespace. Calls to this
122     * method are aggregated into a call to the Tapestry.init() function. Initialization occurs at
123     * {@link InitializationPriority#NORMAL} priority.
124     *
125     * @param functionName
126     *         name of client-side function (within Tapestry.Initializer namespace) to execute
127     * @param parameter
128     *         array of parameters to pass to the client-side function
129     * @since 5.3
130     * @deprecated Deprecated in 5.4; refactor to use {@linkplain #require(String) JavaScript modules} instead
131     */
132    void addInitializerCall(InitializationPriority priority, String functionName, JSONArray parameter);
133
134    /**
135     * Adds a call to a client-side function inside the Tapestry.Initializer namespace. Calls to this
136     * method are aggregated into a call to the Tapestry.init() function. Initialization occurs at
137     * the specified priority.
138     *
139     * @param priority
140     *         priority to use when adding the script
141     * @param functionName
142     *         name of client-side function (within Tapestry.Initializer namespace) to execute
143     * @param parameter
144     *         object to pass to the client-side function
145     * @deprecated Deprecated in 5.4; refactor to use {@linkplain #require(String) JavaScript modules} instead
146     */
147    void addInitializerCall(InitializationPriority priority, String functionName, JSONObject parameter);
148
149    /**
150     * Adds a call to a client-side function inside the Tapestry.Initializer namespace. Calls to this
151     * method are aggregated into a call to the Tapestry.init() function. Initialization occurs at
152     * {@link InitializationPriority#NORMAL} priority.
153     *
154     * @param functionName
155     *         name of client-side function (within Tapestry.Initializer namespace) to execute
156     * @param parameter
157     *         string to pass to function (typically, a client id)
158     * @deprecated Deprecated in 5.4; refactor to use {@linkplain #require(String) JavaScript modules} instead
159     */
160    void addInitializerCall(String functionName, String parameter);
161
162    /**
163     * Adds a call to a client-side function inside the Tapestry.Initializer namespace. Calls to this
164     * method are aggregated into a call to the Tapestry.init() function. Initialization occurs at
165     * the specified priority.
166     *
167     * @param priority
168     *         priority to use when adding the script
169     * @param functionName
170     *         name of client-side function (within Tapestry.Initializer namespace) to execute
171     * @param parameter
172     *         string to pass to function (typically, a client id)
173     * @deprecated Deprecated in 5.4; refactor to use {@linkplain #require(String) JavaScript modules} instead
174     */
175    void addInitializerCall(InitializationPriority priority, String functionName, String parameter);
176
177    /**
178     * Imports a JavaScript library as part of the rendered page. Libraries are added in the order
179     * they are first imported; duplicate imports are ignored. Libraries are added to the page serially
180     * (whereas modules may be loaded in parallel), and all libraries are added before any modules are loaded.
181     * Because of this, it is preferrable to organize your JavaScript into modules, rather than libraries.
182     *
183     * @return this JavaScriptSupport, for further configuration
184     * @see org.apache.tapestry5.annotations.Import
185     */
186    JavaScriptSupport importJavaScriptLibrary(Asset asset);
187
188    /**
189     * A convenience method that wraps the Asset as a {@link StylesheetLink}.
190     *
191     * @param stylesheet
192     *         asset for the stylesheet
193     * @return this JavaScriptSupport, for further configuration
194     * @see #importStylesheet(StylesheetLink)
195     */
196    JavaScriptSupport importStylesheet(Asset stylesheet);
197
198    /**
199     * Imports a Cascading Style Sheet file as part of the rendered page. Stylesheets are added in the
200     * order they are first imported; duplicate imports are ignored. Starting in 5.4, importing a stylesheet
201     * imports the core stack as well (with its stylesheets); this ensures that the imported stylesheet(s) can
202     * override rules from Tapestry's default stylesheets.
203     *
204     * @param stylesheetLink
205     *         encapsulates the link URL plus any additional options
206     * @return this JavaScriptSupport, for further configuration
207     */
208    JavaScriptSupport importStylesheet(StylesheetLink stylesheetLink);
209
210    /**
211     * Imports a {@link JavaScriptStack} by name, a related set of JavaScript libraries and stylesheets.
212     * Stacks are contributions to the {@link JavaScriptStackSource} service. When
213     * {@linkplain SymbolConstants#COMBINE_SCRIPTS JavaScript aggregation} in enabled, the stack will be represented by
214     * a single virtual URL; otherwise the individual asset URLs of the stack
215     * will be added to the document.
216     * <p/>
217     * Please refer to the {@linkplain #importJavaScriptLibrary(Asset) notes about libraries vs. modules}.
218     *
219     * @param stackName
220     *         the name of the stack (case is ignored); the stack must exist
221     * @return this JavaScriptSupport, for further configuration
222     */
223    JavaScriptSupport importStack(String stackName);
224
225    /**
226     * Import a Javascript library with an arbitrary URL.
227     * <p/>
228     * Please refer to the {@linkplain #importJavaScriptLibrary(Asset) notes about libraries vs. modules}.
229     */
230    JavaScriptSupport importJavaScriptLibrary(String libraryURL);
231
232    /**
233     * Invoked to set focus on a rendered field. Takes into account priority, meaning that a field with errors will take
234     * precedence over a merely required field, and over a field that is optional. The value
235     * {@link org.apache.tapestry5.FieldFocusPriority#OVERRIDE} can be used to force a particular field to receive
236     * focus.
237     *
238     * @param priority
239     *         focus is set only if the provided priority is greater than the current priority
240     * @param fieldId
241     *         id of client-side element to take focus
242     */
243    JavaScriptSupport autofocus(FieldFocusPriority priority, String fieldId);
244
245
246    /**
247     * Requires a JavaScript module by name. On the client, this will <code>require()</code> the module and
248     * (optionally) de-reference a function exported by the module (or, treat the module as exporting a single
249     * implicit function). The function will be invoked. Use the returned {@link Initialization} to specify the function name
250     * to invoke, and the parameters to pass to the function.
251     * <p/>
252     * In some cases, a module exports no functions, but performs some initialization (typically, adding document-level
253     * event handlers), in which case a call to require() is sufficient. In cases where the module, or a function
254     * within the module, are invoked with no parameters, the calls will be collapsed into a single invocation.
255     * <p/>
256     * If the module is part of a {@linkplain org.apache.tapestry5.services.javascript.JavaScriptStack#getModules() JavaScript stack},
257     * then the stack will be imported; this is important when {@linkplain SymbolConstants#COMBINE_SCRIPTS JavaScript aggregation is enabled},
258     * but also ensures that libraries in the stack are loaded before the module (for cases where the
259     * module has dependencies on libraries not wrapped as AMD modules).
260     *
261     * @param moduleName
262     *         the name of the module to require
263     * @return Initialization instance, used to configure function name, arguments, etc.
264     * @since 5.4
265     */
266    Initialization require(String moduleName);
267
268    /**
269     * Adds a module configuration callback for this request.
270     *
271     * @param callback
272     *         a {@link ModuleConfigurationCallback}. It cannot be null.
273     * @see DocumentLinker#addModuleConfigurationCallback(ModuleConfigurationCallback)
274     * @since 5.4
275     */
276    void addModuleConfigurationCallback(ModuleConfigurationCallback callback);
277
278}