001: /*
002: * $Id: JavascriptValidatorTag.java 471754 2006-11-06 14:55:09Z husted $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021: package org.apache.struts.taglib.html;
022:
023: import org.apache.commons.validator.Field;
024: import org.apache.commons.validator.Form;
025: import org.apache.commons.validator.ValidatorAction;
026: import org.apache.commons.validator.ValidatorResources;
027: import org.apache.commons.validator.Var;
028: import org.apache.struts.Globals;
029: import org.apache.struts.action.ActionMapping;
030: import org.apache.struts.config.ModuleConfig;
031: import org.apache.struts.taglib.TagUtils;
032: import org.apache.struts.util.MessageResources;
033: import org.apache.struts.validator.Resources;
034: import org.apache.struts.validator.ValidatorPlugIn;
035:
036: import javax.servlet.ServletContext;
037: import javax.servlet.http.HttpServletRequest;
038: import javax.servlet.jsp.JspException;
039: import javax.servlet.jsp.JspWriter;
040: import javax.servlet.jsp.PageContext;
041: import javax.servlet.jsp.tagext.BodyTagSupport;
042:
043: import java.io.IOException;
044:
045: import java.util.ArrayList;
046: import java.util.Collections;
047: import java.util.Comparator;
048: import java.util.Iterator;
049: import java.util.List;
050: import java.util.Locale;
051: import java.util.Map;
052: import java.util.StringTokenizer;
053:
054: /**
055: * Custom tag that generates JavaScript for client side validation based on
056: * the validation rules loaded by the <code>ValidatorPlugIn</code> defined in
057: * the struts-config.xml file.
058: *
059: * @version $Rev: 471754 $ $Date: 2006-11-06 08:55:09 -0600 (Mon, 06 Nov 2006) $
060: * @since Struts 1.1
061: */
062: public class JavascriptValidatorTag extends BodyTagSupport {
063: /**
064: * A Comparator to use when sorting ValidatorAction objects.
065: */
066: private static final Comparator actionComparator = new Comparator() {
067: public int compare(Object o1, Object o2) {
068: ValidatorAction va1 = (ValidatorAction) o1;
069: ValidatorAction va2 = (ValidatorAction) o2;
070:
071: if (((va1.getDepends() == null) || (va1.getDepends()
072: .length() == 0))
073: && ((va2.getDepends() == null) || (va2.getDepends()
074: .length() == 0))) {
075: return 0;
076: } else if (((va1.getDepends() != null) && (va1.getDepends()
077: .length() > 0))
078: && ((va2.getDepends() == null) || (va2.getDepends()
079: .length() == 0))) {
080: return 1;
081: } else if (((va1.getDepends() == null) || (va1.getDepends()
082: .length() == 0))
083: && ((va2.getDepends() != null) && (va2.getDepends()
084: .length() > 0))) {
085: return -1;
086: } else {
087: return va1.getDependencyList().size()
088: - va2.getDependencyList().size();
089: }
090: }
091: };
092:
093: /**
094: * The start of the HTML comment hiding JavaScript from old browsers.
095: *
096: * @since Struts 1.2
097: */
098: protected static final String HTML_BEGIN_COMMENT = "\n<!-- Begin \n";
099:
100: /**
101: * The end of the HTML comment hiding JavaScript from old browsers.
102: *
103: * @since Struts 1.2
104: */
105: protected static final String HTML_END_COMMENT = "//End --> \n";
106:
107: /**
108: * The line ending string.
109: */
110: protected static String lineEnd = System
111: .getProperty("line.separator");
112:
113: // ----------------------------------------------------------- Properties
114:
115: /**
116: * The servlet context attribute key for our resources.
117: */
118: protected String bundle = Globals.MESSAGES_KEY;
119:
120: /**
121: * The name of the form that corresponds with the action name in
122: * struts-config.xml. Specifying a form name places a <script>
123: * </script> around the javascript.
124: */
125: protected String formName = null;
126:
127: /**
128: * formName is used for both Javascript and non-javascript validations.
129: * For the javascript validations, there is the possibility that we will
130: * be rewriting the formName (if it is a ValidatorActionForm instead of
131: * just a ValidatorForm) so we need another variable to hold the formName
132: * just for javascript usage.
133: */
134: protected String jsFormName = null;
135:
136: /**
137: * The current page number of a multi-part form. Only valid when the
138: * formName attribute is set.
139: */
140: protected int page = 0;
141:
142: /**
143: * This will be used as is for the JavaScript validation method name if it
144: * has a value. This is the method name of the main JavaScript method
145: * that the form calls to perform validations.
146: */
147: protected String methodName = null;
148:
149: /**
150: * Include language attribute in the <script> element. This
151: * property is ignored in XHTML mode.
152: *
153: * @since Struts 1.2
154: */
155: protected boolean scriptLanguage = true;
156:
157: /**
158: * The static JavaScript methods will only be printed if this is set to
159: * "true".
160: */
161: protected String staticJavascript = "true";
162:
163: /**
164: * The dynamic JavaScript objects will only be generated if this is set to
165: * "true".
166: */
167: protected String dynamicJavascript = "true";
168:
169: /**
170: * The src attribute for html script element (used to include an external
171: * script resource). The src attribute is only recognized when the
172: * formName attribute is specified.
173: */
174: protected String src = null;
175:
176: /**
177: * The JavaScript methods will enclosed with html comments if this is set
178: * to "true".
179: */
180: protected String htmlComment = "true";
181:
182: /**
183: * Hide JavaScript methods in a CDATA section for XHTML when "true".
184: */
185: protected String cdata = "true";
186:
187: /**
188: * Gets the key (form name) that will be used to retrieve a set of
189: * validation rules to be performed on the bean passed in for validation.
190: */
191: public String getFormName() {
192: return formName;
193: }
194:
195: /**
196: * Sets the key (form name) that will be used to retrieve a set of
197: * validation rules to be performed on the bean passed in for validation.
198: * Specifying a form name places a <script> </script> tag
199: * around the javascript.
200: */
201: public void setFormName(String formName) {
202: this .formName = formName;
203: }
204:
205: /**
206: * @return Returns the jsFormName.
207: */
208: public String getJsFormName() {
209: return jsFormName;
210: }
211:
212: /**
213: * @param jsFormName The jsFormName to set.
214: */
215: public void setJsFormName(String jsFormName) {
216: this .jsFormName = jsFormName;
217: }
218:
219: /**
220: * Gets the current page number of a multi-part form. Only field
221: * validations with a matching page numer will be generated that match the
222: * current page number. Only valid when the formName attribute is set.
223: */
224: public int getPage() {
225: return page;
226: }
227:
228: /**
229: * Sets the current page number of a multi-part form. Only field
230: * validations with a matching page numer will be generated that match the
231: * current page number. Only valid when the formName attribute is set.
232: */
233: public void setPage(int page) {
234: this .page = page;
235: }
236:
237: /**
238: * Gets the method name that will be used for the Javascript validation
239: * method name if it has a value. This overrides the auto-generated
240: * method name based on the key (form name) passed in.
241: */
242: public String getMethod() {
243: return methodName;
244: }
245:
246: /**
247: * Sets the method name that will be used for the Javascript validation
248: * method name if it has a value. This overrides the auto-generated
249: * method name based on the key (form name) passed in.
250: */
251: public void setMethod(String methodName) {
252: this .methodName = methodName;
253: }
254:
255: /**
256: * Gets whether or not to generate the static JavaScript. If this is set
257: * to 'true', which is the default, the static JavaScript will be
258: * generated.
259: */
260: public String getStaticJavascript() {
261: return staticJavascript;
262: }
263:
264: /**
265: * Sets whether or not to generate the static JavaScript. If this is set
266: * to 'true', which is the default, the static JavaScript will be
267: * generated.
268: */
269: public void setStaticJavascript(String staticJavascript) {
270: this .staticJavascript = staticJavascript;
271: }
272:
273: /**
274: * Gets whether or not to generate the dynamic JavaScript. If this is set
275: * to 'true', which is the default, the dynamic JavaScript will be
276: * generated.
277: */
278: public String getDynamicJavascript() {
279: return dynamicJavascript;
280: }
281:
282: /**
283: * Sets whether or not to generate the dynamic JavaScript. If this is set
284: * to 'true', which is the default, the dynamic JavaScript will be
285: * generated.
286: */
287: public void setDynamicJavascript(String dynamicJavascript) {
288: this .dynamicJavascript = dynamicJavascript;
289: }
290:
291: /**
292: * Gets whether or not to delimit the JavaScript with html comments. If
293: * this is set to 'true', which is the default, the htmlComment will be
294: * surround the JavaScript.
295: */
296: public String getHtmlComment() {
297: return htmlComment;
298: }
299:
300: /**
301: * Sets whether or not to delimit the JavaScript with html comments. If
302: * this is set to 'true', which is the default, the htmlComment will be
303: * surround the JavaScript.
304: */
305: public void setHtmlComment(String htmlComment) {
306: this .htmlComment = htmlComment;
307: }
308:
309: /**
310: * Gets the src attribute's value when defining the html script element.
311: */
312: public String getSrc() {
313: return src;
314: }
315:
316: /**
317: * Sets the src attribute's value when defining the html script element.
318: * The src attribute is only recognized when the formName attribute is
319: * specified.
320: */
321: public void setSrc(String src) {
322: this .src = src;
323: }
324:
325: /**
326: * Sets the servlet context attribute key for our resources.
327: */
328: public String getBundle() {
329: return bundle;
330: }
331:
332: /**
333: * Gets the servlet context attribute key for our resources.
334: */
335: public void setBundle(String bundle) {
336: this .bundle = bundle;
337: }
338:
339: /**
340: * Render the JavaScript for to perform validations based on the form
341: * name.
342: *
343: * @throws JspException if a JSP exception has occurred
344: */
345: public int doStartTag() throws JspException {
346: JspWriter writer = pageContext.getOut();
347:
348: try {
349: writer.print(this .renderJavascript());
350: } catch (IOException e) {
351: throw new JspException(e.getMessage());
352: }
353:
354: return EVAL_BODY_TAG;
355: }
356:
357: /**
358: * Returns fully rendered JavaScript.
359: *
360: * @since Struts 1.2
361: */
362: protected String renderJavascript() throws JspException {
363: StringBuffer results = new StringBuffer();
364:
365: ModuleConfig config = TagUtils.getInstance().getModuleConfig(
366: pageContext);
367: ValidatorResources resources = (ValidatorResources) pageContext
368: .getAttribute(ValidatorPlugIn.VALIDATOR_KEY
369: + config.getPrefix(),
370: PageContext.APPLICATION_SCOPE);
371:
372: if (resources == null) {
373: throw new JspException(
374: "ValidatorResources not found in application scope under key \""
375: + ValidatorPlugIn.VALIDATOR_KEY
376: + config.getPrefix() + "\"");
377: }
378:
379: Locale locale = TagUtils.getInstance().getUserLocale(
380: this .pageContext, null);
381:
382: Form form = null;
383: if ("true".equalsIgnoreCase(dynamicJavascript)) {
384: form = resources.getForm(locale, formName);
385: if (form == null) {
386: throw new JspException("No form found under '"
387: + formName + "' in locale '" + locale
388: + "'. A form must be defined in the "
389: + "Commons Validator configuration when "
390: + "dynamicJavascript=\"true\" is set.");
391: }
392: }
393:
394: if (form != null) {
395: if ("true".equalsIgnoreCase(dynamicJavascript)) {
396: results.append(this .createDynamicJavascript(config,
397: resources, locale, form));
398: } else if ("true".equalsIgnoreCase(staticJavascript)) {
399: results.append(this .renderStartElement());
400:
401: if ("true".equalsIgnoreCase(htmlComment)) {
402: results.append(HTML_BEGIN_COMMENT);
403: }
404: }
405: }
406:
407: if ("true".equalsIgnoreCase(staticJavascript)) {
408: results.append(getJavascriptStaticMethods(resources));
409: }
410:
411: if ((form != null)
412: && ("true".equalsIgnoreCase(dynamicJavascript) || "true"
413: .equalsIgnoreCase(staticJavascript))) {
414: results.append(getJavascriptEnd());
415: }
416:
417: return results.toString();
418: }
419:
420: /**
421: * Generates the dynamic JavaScript for the form.
422: *
423: * @param config
424: * @param resources
425: * @param locale
426: * @param form
427: */
428: private String createDynamicJavascript(ModuleConfig config,
429: ValidatorResources resources, Locale locale, Form form)
430: throws JspException {
431: StringBuffer results = new StringBuffer();
432:
433: MessageResources messages = TagUtils.getInstance()
434: .retrieveMessageResources(pageContext, bundle, true);
435:
436: HttpServletRequest request = (HttpServletRequest) pageContext
437: .getRequest();
438: ServletContext application = pageContext.getServletContext();
439:
440: List actions = this .createActionList(resources, form);
441:
442: final String methods = this .createMethods(actions, this
443: .stopOnError(config));
444:
445: String formName = form.getName();
446:
447: jsFormName = formName;
448:
449: if (jsFormName.charAt(0) == '/') {
450: String mappingName = TagUtils.getInstance()
451: .getActionMappingName(jsFormName);
452: ActionMapping mapping = (ActionMapping) config
453: .findActionConfig(mappingName);
454:
455: if (mapping == null) {
456: JspException e = new JspException(messages.getMessage(
457: "formTag.mapping", mappingName));
458:
459: pageContext.setAttribute(Globals.EXCEPTION_KEY, e,
460: PageContext.REQUEST_SCOPE);
461: throw e;
462: }
463:
464: jsFormName = mapping.getAttribute();
465: }
466:
467: results.append(this .getJavascriptBegin(methods));
468:
469: for (Iterator i = actions.iterator(); i.hasNext();) {
470: ValidatorAction va = (ValidatorAction) i.next();
471: int jscriptVar = 0;
472: String functionName = null;
473:
474: if ((va.getJsFunctionName() != null)
475: && (va.getJsFunctionName().length() > 0)) {
476: functionName = va.getJsFunctionName();
477: } else {
478: functionName = va.getName();
479: }
480:
481: results.append(" function " + jsFormName + "_"
482: + functionName + " () { \n");
483:
484: for (Iterator x = form.getFields().iterator(); x.hasNext();) {
485: Field field = (Field) x.next();
486:
487: // Skip indexed fields for now until there is a good way to
488: // handle error messages (and the length of the list (could
489: // retrieve from scope?))
490: if (field.isIndexed() || (field.getPage() != page)
491: || !field.isDependency(va.getName())) {
492: continue;
493: }
494:
495: String message = Resources.getMessage(application,
496: request, messages, locale, va, field);
497:
498: message = (message != null) ? message : "";
499:
500: // prefix variable with 'a' to make it a legal identifier
501: results.append(" this.a" + jscriptVar++
502: + " = new Array(\"" + field.getKey() + "\", \""
503: + escapeQuotes(message) + "\", ");
504:
505: results.append("new Function (\"varName\", \"");
506:
507: Map vars = field.getVars();
508:
509: // Loop through the field's variables.
510: Iterator varsIterator = vars.keySet().iterator();
511:
512: while (varsIterator.hasNext()) {
513: String varName = (String) varsIterator.next();
514: Var var = (Var) vars.get(varName);
515: String varValue = Resources.getVarValue(var,
516: application, request, false);
517: String jsType = var.getJsType();
518:
519: // skip requiredif variables field, fieldIndexed, fieldTest,
520: // fieldValue
521: if (varName.startsWith("field")) {
522: continue;
523: }
524:
525: String varValueEscaped = escapeJavascript(varValue);
526:
527: if (Var.JSTYPE_INT.equalsIgnoreCase(jsType)) {
528: results.append("this." + varName + "="
529: + varValueEscaped + "; ");
530: } else if (Var.JSTYPE_REGEXP
531: .equalsIgnoreCase(jsType)) {
532: results.append("this." + varName + "=/"
533: + varValueEscaped + "/; ");
534: } else if (Var.JSTYPE_STRING
535: .equalsIgnoreCase(jsType)) {
536: results.append("this." + varName + "='"
537: + varValueEscaped + "'; ");
538:
539: // So everyone using the latest format doesn't need to
540: // change their xml files immediately.
541: } else if ("mask".equalsIgnoreCase(varName)) {
542: results.append("this." + varName + "=/"
543: + varValueEscaped + "/; ");
544: } else {
545: results.append("this." + varName + "='"
546: + varValueEscaped + "'; ");
547: }
548: }
549:
550: results.append(" return this[varName];\"));\n");
551: }
552:
553: results.append(" } \n\n");
554: }
555:
556: return results.toString();
557: }
558:
559: private String escapeQuotes(String in) {
560: if ((in == null) || (in.indexOf("\"") == -1)) {
561: return in;
562: }
563:
564: StringBuffer buffer = new StringBuffer();
565: StringTokenizer tokenizer = new StringTokenizer(in, "\"", true);
566:
567: while (tokenizer.hasMoreTokens()) {
568: String token = tokenizer.nextToken();
569:
570: if (token.equals("\"")) {
571: buffer.append("\\");
572: }
573:
574: buffer.append(token);
575: }
576:
577: return buffer.toString();
578: }
579:
580: /**
581: * <p>Backslash-escapes the following characters from the input string:
582: * ", ', \, \r, \n.</p>
583: *
584: * <p>This method escapes characters that will result in an invalid
585: * Javascript statement within the validator Javascript.</p>
586: *
587: * @param str The string to escape.
588: * @return The string <code>s</code> with each instance of a double quote,
589: * single quote, backslash, carriage-return, or line feed escaped
590: * with a leading backslash.
591: */
592: private String escapeJavascript(String str) {
593: if (str == null) {
594: return null;
595: }
596:
597: int length = str.length();
598:
599: if (length == 0) {
600: return str;
601: }
602:
603: // guess at how many chars we'll be adding...
604: StringBuffer out = new StringBuffer(length + 4);
605:
606: // run through the string escaping sensitive chars
607: for (int i = 0; i < length; i++) {
608: char c = str.charAt(i);
609:
610: if ((c == '"') || (c == '\'') || (c == '\\') || (c == '\n')
611: || (c == '\r')) {
612: out.append('\\');
613: }
614:
615: out.append(c);
616: }
617:
618: return out.toString();
619: }
620:
621: /**
622: * Determines if validations should stop on an error.
623: *
624: * @param config The <code>ModuleConfig</code> used to lookup the
625: * stopOnError setting.
626: * @return <code>true</code> if validations should stop on errors.
627: */
628: private boolean stopOnError(ModuleConfig config) {
629: Object stopOnErrorObj = pageContext.getAttribute(
630: ValidatorPlugIn.STOP_ON_ERROR_KEY + '.'
631: + config.getPrefix(),
632: PageContext.APPLICATION_SCOPE);
633:
634: boolean stopOnError = true;
635:
636: if (stopOnErrorObj instanceof Boolean) {
637: stopOnError = ((Boolean) stopOnErrorObj).booleanValue();
638: }
639:
640: return stopOnError;
641: }
642:
643: /**
644: * Creates the JavaScript methods list from the given actions.
645: *
646: * @param actions A List of ValidatorAction objects.
647: * @param stopOnError If true, behaves like released version of struts 1.1
648: * and stops after first error. If false, evaluates all
649: * validations.
650: * @return JavaScript methods.
651: */
652: private String createMethods(List actions, boolean stopOnError) {
653: StringBuffer methods = new StringBuffer();
654: final String methodOperator = stopOnError ? " && " : " & ";
655:
656: Iterator iter = actions.iterator();
657:
658: while (iter.hasNext()) {
659: ValidatorAction va = (ValidatorAction) iter.next();
660:
661: if (methods.length() > 0) {
662: methods.append(methodOperator);
663: }
664:
665: methods.append(va.getMethod()).append("(form)");
666: }
667:
668: return methods.toString();
669: }
670:
671: /**
672: * Get List of actions for the given Form.
673: *
674: * @param resources
675: * @param form
676: * @return A sorted List of ValidatorAction objects.
677: */
678: private List createActionList(ValidatorResources resources,
679: Form form) {
680: List actionMethods = new ArrayList();
681:
682: Iterator iterator = form.getFields().iterator();
683:
684: while (iterator.hasNext()) {
685: Field field = (Field) iterator.next();
686:
687: for (Iterator x = field.getDependencyList().iterator(); x
688: .hasNext();) {
689: Object o = x.next();
690:
691: if ((o != null) && !actionMethods.contains(o)) {
692: actionMethods.add(o);
693: }
694: }
695: }
696:
697: List actions = new ArrayList();
698:
699: // Create list of ValidatorActions based on actionMethods
700: iterator = actionMethods.iterator();
701:
702: while (iterator.hasNext()) {
703: String depends = (String) iterator.next();
704: ValidatorAction va = resources.getValidatorAction(depends);
705:
706: // throw nicer NPE for easier debugging
707: if (va == null) {
708: throw new NullPointerException("Depends string \""
709: + depends
710: + "\" was not found in validator-rules.xml.");
711: }
712:
713: if ((va.getJavascript() != null)
714: && (va.getJavascript().length() > 0)) {
715: actions.add(va);
716: } else {
717: iterator.remove();
718: }
719: }
720:
721: Collections.sort(actions, actionComparator);
722:
723: return actions;
724: }
725:
726: /**
727: * Release any acquired resources.
728: */
729: public void release() {
730: super .release();
731: bundle = Globals.MESSAGES_KEY;
732: formName = null;
733: jsFormName = null;
734: page = 0;
735: methodName = null;
736: staticJavascript = "true";
737: dynamicJavascript = "true";
738: htmlComment = "true";
739: cdata = "true";
740: src = null;
741: }
742:
743: /**
744: * Returns the opening script element and some initial javascript.
745: */
746: protected String getJavascriptBegin(String methods) {
747: StringBuffer sb = new StringBuffer();
748: String name = jsFormName.replace('/', '_'); // remove any '/' characters
749:
750: name = jsFormName.substring(0, 1).toUpperCase()
751: + jsFormName.substring(1, jsFormName.length());
752:
753: sb.append(this .renderStartElement());
754:
755: if (this .isXhtml() && "true".equalsIgnoreCase(this .cdata)) {
756: sb.append("//<![CDATA[\r\n");
757: }
758:
759: if (!this .isXhtml() && "true".equals(htmlComment)) {
760: sb.append(HTML_BEGIN_COMMENT);
761: }
762:
763: sb.append("\n var bCancel = false; \n\n");
764:
765: if ((methodName == null) || (methodName.length() == 0)) {
766: sb.append(" function validate" + name + "(form) { \n");
767: } else {
768: sb.append(" function " + methodName + "(form) { \n");
769: }
770:
771: sb.append(" if (bCancel) { \n");
772: sb.append(" return true; \n");
773: sb.append(" } else { \n");
774:
775: // Always return true if there aren't any Javascript validation methods
776: if ((methods == null) || (methods.length() == 0)) {
777: sb.append(" return true; \n");
778: } else {
779: sb.append(" var formValidationResult; \n");
780: sb.append(" formValidationResult = " + methods
781: + "; \n");
782: if (methods.indexOf("&&") >= 0) {
783: sb
784: .append(" return (formValidationResult); \n");
785: } else {
786: //Making Sure that Bitwise operator works:
787: sb
788: .append(" return (formValidationResult == 1); \n");
789: }
790: }
791: sb.append(" } \n");
792: sb.append(" } \n\n");
793:
794: return sb.toString();
795: }
796:
797: protected String getJavascriptStaticMethods(
798: ValidatorResources resources) {
799: StringBuffer sb = new StringBuffer();
800:
801: sb.append("\n\n");
802:
803: Iterator actions = resources.getValidatorActions().values()
804: .iterator();
805:
806: while (actions.hasNext()) {
807: ValidatorAction va = (ValidatorAction) actions.next();
808:
809: if (va != null) {
810: String javascript = va.getJavascript();
811:
812: if ((javascript != null) && (javascript.length() > 0)) {
813: sb.append(javascript + "\n");
814: }
815: }
816: }
817:
818: return sb.toString();
819: }
820:
821: /**
822: * Returns the closing script element.
823: */
824: protected String getJavascriptEnd() {
825: StringBuffer sb = new StringBuffer();
826:
827: sb.append("\n");
828:
829: if (!this .isXhtml() && "true".equals(htmlComment)) {
830: sb.append(HTML_END_COMMENT);
831: }
832:
833: if (this .isXhtml() && "true".equalsIgnoreCase(this .cdata)) {
834: sb.append("//]]>\r\n");
835: }
836:
837: sb.append("</script>\n\n");
838:
839: return sb.toString();
840: }
841:
842: /**
843: * Constructs the beginning <script> element depending on XHTML
844: * status.
845: *
846: * @since Struts 1.2
847: */
848: protected String renderStartElement() {
849: StringBuffer start = new StringBuffer(
850: "<script type=\"text/javascript\"");
851:
852: // there is no language attribute in XHTML
853: if (!this .isXhtml() && this .scriptLanguage) {
854: start.append(" language=\"Javascript1.1\"");
855: }
856:
857: if (this .src != null) {
858: start.append(" src=\"" + src + "\"");
859: }
860:
861: start.append("> \n");
862:
863: return start.toString();
864: }
865:
866: /**
867: * Returns true if this is an xhtml page.
868: */
869: private boolean isXhtml() {
870: return TagUtils.getInstance().isXhtml(this .pageContext);
871: }
872:
873: /**
874: * Returns the cdata setting "true" or "false".
875: *
876: * @return String - "true" if JavaScript will be hidden in a CDATA
877: * section
878: */
879: public String getCdata() {
880: return cdata;
881: }
882:
883: /**
884: * Sets the cdata status.
885: *
886: * @param cdata The cdata to set
887: */
888: public void setCdata(String cdata) {
889: this .cdata = cdata;
890: }
891:
892: /**
893: * Gets whether or not the <script> element will include the
894: * language attribute.
895: *
896: * @return true if language attribute will be included.
897: * @since Struts 1.2
898: */
899: public boolean getScriptLanguage() {
900: return this .scriptLanguage;
901: }
902:
903: /**
904: * Sets whether or not the <script> element will include the
905: * language attribute.
906: *
907: * @since Struts 1.2
908: */
909: public void setScriptLanguage(boolean scriptLanguage) {
910: this.scriptLanguage = scriptLanguage;
911: }
912: }
|