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