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