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 }