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