001 // Copyright 2004, 2005 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 package org.apache.tapestry.dojo;
015
016 import java.util.Locale;
017
018 import org.apache.hivemind.util.Defense;
019 import org.apache.tapestry.IAsset;
020 import org.apache.tapestry.IMarkupWriter;
021 import org.apache.tapestry.IPage;
022 import org.apache.tapestry.IRender;
023 import org.apache.tapestry.IRequestCycle;
024 import org.apache.tapestry.json.JSONLiteral;
025 import org.apache.tapestry.json.JSONObject;
026
027
028 /**
029 * The default rendering delegate responsible for include the dojo sources in
030 * to the {@link org.apache.tapestry.html.Shell} component.
031 */
032 public class AjaxShellDelegate implements IRender {
033
034 /** Client side debug log level. */
035 public static final String BROWSER_LOG_DEBUG="DEBUG";
036 /** Client side info log level. */
037 public static final String BROWSER_LOG_INFO="INFO";
038 /** Client side warning log level. */
039 public static final String BROWSER_LOG_WARNING="WARNING";
040 /** Client side error log level. */
041 public static final String BROWSER_LOG_ERROR="ERROR";
042 /** Client side critical log level. */
043 public static final String BROWSER_LOG_CRITICAL="CRITICAL";
044
045 private static final String SYSTEM_NEWLINE= (String)java.security.AccessController.doPrivileged(
046 new sun.security.action.GetPropertyAction("line.separator"));
047
048 /** Default list of pre-bundled dojo supported locales. */
049 protected String[] SUPPORTED_LOCALES = { "en-us", "de-de", "de", "en-gb",
050 "es-es", "es", "fr-fr", "fr", "zh-cn",
051 "zh-tw", "zh" , "it-it", "it", "ja-jp",
052 "ja", "ko-kr", "ko", "pt-br", "pt", "en", "xx"};
053
054 private IAsset _dojoSource;
055
056 private IAsset _dojoFormSource;
057
058 private IAsset _dojoWidgetSource;
059
060 private IAsset _dojoPath;
061
062 private IAsset _tapestrySource;
063
064 private IAsset _tapestryPath;
065
066 private boolean _parseWidgets;
067
068 private String _browserLogLevel = BROWSER_LOG_WARNING;
069
070 private boolean _debug;
071
072 private String _debugContainerId;
073
074 private boolean _consoleEnabled;
075
076 private boolean _preventBackButtonFix;
077
078 private boolean _debugAtAllCosts;
079
080 private String _searchIds;
081
082 /**
083 * {@inheritDoc}
084 */
085 public void render(IMarkupWriter writer, IRequestCycle cycle)
086 {
087 // first configure dojo, has to happen before package include
088
089 JSONObject dojoConfig = new JSONObject();
090
091 // Debugging configuration , debugAtAlCosts causes the individual
092 // .js files to included in the document head so that javascript errors
093 // are able to resolve to the context of the file instead of just "dojo.js"
094
095 if (_debug)
096 {
097 dojoConfig.put("isDebug", _debug);
098 }
099
100 if (_debugAtAllCosts)
101 dojoConfig.put("debugAtAllCosts", _debugAtAllCosts);
102 if (_debugContainerId != null)
103 dojoConfig.put("debugContainerId", _debugContainerId);
104
105 IPage page = cycle.getPage();
106
107 // The key to resolving everything out of the asset service
108
109 if (_dojoPath!=null)
110 {
111 dojoConfig.put("baseRelativePath", _dojoPath.buildURL());
112 }
113
114 if (page.hasFormComponents())
115 {
116 dojoConfig.put("preventBackButtonFix", _preventBackButtonFix);
117 }
118
119 dojoConfig.put("parseWidgets", _parseWidgets);
120 if (_searchIds != null)
121 dojoConfig.put("searchIds", new JSONLiteral(_searchIds));
122
123 // Supports setting up locale in dojo environment to match the requested page locale.
124 // (for things that use these settings, like DropdownDatePicker / date parsing / etc..
125
126 Locale locale = cycle.getPage().getLocale();
127
128 String localeStr = locale.getLanguage().toLowerCase()
129 + ((locale.getCountry() != null && locale.getCountry().trim().length() > 0)
130 ? "-" + locale.getCountry().toLowerCase()
131 : "");
132
133 if (isLocaleSupported(localeStr))
134 {
135 dojoConfig.put("locale", localeStr);
136 }
137
138 // Write the required script includes and dojo.requires
139
140 StringBuffer str = new StringBuffer("<script type=\"text/javascript\">");
141 str.append("djConfig = ").append(dojoConfig.toString())
142 .append(" </script>")
143 .append(SYSTEM_NEWLINE).append(SYSTEM_NEWLINE);
144
145 // include the core dojo.js package
146
147 if (_dojoSource!=null)
148 {
149 str.append("<script type=\"text/javascript\" src=\"")
150 .append(_dojoSource.buildURL()).append("\"></script>");
151 }
152
153 if (page.hasFormComponents() && _dojoFormSource!=null)
154 {
155 str.append("<script type=\"text/javascript\" src=\"")
156 .append(_dojoFormSource.buildURL()).append("\"></script>");
157 }
158
159 if (page.hasWidgets() && _dojoWidgetSource!=null)
160 {
161 str.append("<script type=\"text/javascript\" src=\"")
162 .append(_dojoWidgetSource.buildURL()).append("\"></script>");
163 }
164
165 // configure basic dojo properties , logging includes
166
167 if (_debug)
168 {
169 String logRequire = _consoleEnabled ? "dojo.require(\"dojo.debug.console\");" + SYSTEM_NEWLINE
170 : "dojo.require(\"dojo.logging.Logger\");" + SYSTEM_NEWLINE;
171
172 str.append(SYSTEM_NEWLINE).append("<script type=\"text/javascript\">").append(SYSTEM_NEWLINE);
173 str.append(logRequire)
174 .append("dojo.log.setLevel(dojo.log.getLevel(\"").append(_browserLogLevel)
175 .append("\"));").append(SYSTEM_NEWLINE)
176 .append("</script>");
177 }
178
179 // module path registration to tapestry javascript sources
180
181 if (_tapestryPath!=null)
182 {
183 String tapestryUrl = _tapestryPath.buildURL();
184 if (tapestryUrl.endsWith("/"))
185 {
186 tapestryUrl = tapestryUrl.substring(0, tapestryUrl.length() - 1);
187 }
188
189 str.append(SYSTEM_NEWLINE).append("<script type=\"text/javascript\">").append(SYSTEM_NEWLINE)
190 .append("dojo.registerModulePath(\"tapestry\", \"")
191 .append(tapestryUrl).append("\");").append(SYSTEM_NEWLINE);
192 str.append("</script>").append(SYSTEM_NEWLINE);
193 }
194
195 // include core tapestry.js package
196
197 if (_tapestrySource!=null)
198 {
199 str.append("<script type=\"text/javascript\" src=\"")
200 .append(_tapestrySource.buildURL()).append("\"></script>");
201 }
202
203 // namespace registration
204
205 str.append(SYSTEM_NEWLINE).append("<script type=\"text/javascript\">").append(SYSTEM_NEWLINE);
206 str.append("dojo.require(\"tapestry.namespace\");").append(SYSTEM_NEWLINE)
207 .append("tapestry.requestEncoding='")
208 .append(cycle.getEngine().getOutputEncoding()).append("';")
209 .append(SYSTEM_NEWLINE).append("</script>");
210
211 writer.printRaw(str.toString());
212 writer.println();
213 }
214
215 /**
216 * Checks if the provided locale string matches one of the predefined {@link #SUPPORTED_LOCALES}
217 * in the dojo javascript library.
218 *
219 * @param locale
220 * The Dojo formatted locale string to check.
221 *
222 * @return True if locale is supported and ok to define in dojoConfig - false otherwise.
223 */
224 protected boolean isLocaleSupported(String locale)
225 {
226 if (locale == null)
227 return false;
228
229 for (int i=0; i < SUPPORTED_LOCALES.length; i++)
230 {
231 if (locale.equals(SUPPORTED_LOCALES[i]))
232 return true;
233 }
234
235 return false;
236 }
237
238 /**
239 * Sets the dojo logging level. Similar to log4j style
240 * log levels.
241 * @param level The string constant for the level, valid values
242 * are:
243 * <p>
244 * <ul>
245 * <li>{@link #BROWSER_LOG_DEBUG}</li>
246 * <li>{@link #BROWSER_LOG_INFO}</li>
247 * <li>{@link #BROWSER_LOG_WARNING}</li>
248 * <li>{@link #BROWSER_LOG_ERROR}</li>
249 * <li>{@link #BROWSER_LOG_CRITICAL}</li>
250 * </ul>
251 * </p>
252 */
253 public void setLogLevel(String level)
254 {
255 Defense.notNull("level", level);
256
257 _browserLogLevel = level;
258 }
259
260 /**
261 * Allows for turning browser debugging on/off.
262 *
263 * @param debug If false, no logging output will be written.
264 */
265 public void setDebug(boolean debug)
266 {
267 _debug = debug;
268 }
269
270 /**
271 * Turns off deep context level javascript debugging mode for dojo. This means
272 * that exceptions/debug statements will show you line numbers from the actual
273 * javascript file that generated them instead of the normal default which is
274 * usually bootstrap.js .
275 *
276 * <p>The default value is false if not set.</p>
277 *
278 * <p>
279 * People should be wary of turning this on as it may cause problems
280 * under certain conditions, and you definitely don't ever want this
281 * on in production.
282 * </p>
283 *
284 * @param value If true deep debugging will be turned on.
285 */
286 public void setDebugAtAllCosts(boolean value)
287 {
288 _debugAtAllCosts = value;
289 }
290
291 /**
292 * Sets the html element node id of the element you would like all browser
293 * debug content to go to.
294 *
295 * @param debugContainerId the debugContainerId to set
296 */
297 public void setDebugContainerId(String debugContainerId)
298 {
299 _debugContainerId = debugContainerId;
300 }
301
302 /**
303 * Enables/disables the dojo.debug.console functionality which should redirect
304 * most logging messages to your browsers javascript console. (if it supports
305 * one).
306 *
307 * <p>
308 * The debug console is disabled by default. Currently known supported
309 * browsers are FireFox(having FireBug extension helps a great deal)/Opera/Safari.
310 * </p>
311 *
312 * @param enabled Whether or not the enable debug console.
313 */
314 public void setConsoleEnabled(boolean enabled)
315 {
316 _consoleEnabled = enabled;
317 }
318
319 /**
320 * Sets the dojo preventBackButtonFix djConfig configuration. This should
321 * typically be avoided but is provided for flexibility.
322 *
323 * @param prevent
324 * Whether or not to prevent back button fix.
325 */
326 public void setPreventBackButtonFix(boolean prevent)
327 {
328 _preventBackButtonFix = prevent;
329 }
330
331 /**
332 * Tells dojo whether or not to parse widgets by traversing the entire
333 * dom node of your document. It is highly reccomended that you keep this
334 * at its default value of false.
335 *
336 * @param parseWidgets the parseWidgets to set
337 */
338 public void setParseWidgets(boolean parseWidgets)
339 {
340 _parseWidgets = parseWidgets;
341 }
342
343 /**
344 * Provides a way to have dojo automatically parse a known set of page
345 * widgets without enabling full automatic parsing.
346 *
347 * @param searchIds the html ids within which to search for widgets
348 */
349 public void setSearchIds(String searchIds)
350 {
351 _searchIds = searchIds;
352 }
353
354 /**
355 * Sets a valid path to the base dojo javascript installation
356 * directory.
357 *
358 * @param dojoSource
359 * Path to dojo source directory core "dojo.js" file.
360 */
361 public void setDojoSource(IAsset dojoSource)
362 {
363 _dojoSource = dojoSource;
364 }
365
366 public void setDojoFormSource(IAsset formSource)
367 {
368 _dojoFormSource = formSource;
369 }
370
371 public void setDojoWidgetSource(IAsset widgetSource)
372 {
373 _dojoWidgetSource = widgetSource;
374 }
375
376 /**
377 * Sets the dojo baseRelativePath value.
378 *
379 * @param dojoPath
380 * The base path to dojo directory.
381 */
382 public void setDojoPath(IAsset dojoPath)
383 {
384 _dojoPath = dojoPath;
385 }
386
387 /**
388 * Sets a valid base path to resolve tapestry core.js.
389 *
390 * @param tapestrySource
391 * Main tapestry core.js file.
392 */
393 public void setTapestrySource(IAsset tapestrySource)
394 {
395 _tapestrySource = tapestrySource;
396 }
397
398 /**
399 * Sets the path to the tapestry javascript modules. (Needed for dojo to resolve the
400 * path to tapestry javascript, esp when overriding the default bundled dojo.)
401 *
402 * @param tapestryPath The path to tapestry.
403 */
404 public void setTapestryPath(IAsset tapestryPath)
405 {
406 _tapestryPath = tapestryPath;
407 }
408 }