View Javadoc

1   package org.apache.maven.plugins.surefire.report;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.text.NumberFormat;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Locale;
27  import java.util.Map;
28  import java.util.ResourceBundle;
29  import java.util.StringTokenizer;
30  import org.apache.maven.doxia.markup.HtmlMarkup;
31  import org.apache.maven.doxia.sink.Sink;
32  import org.apache.maven.doxia.sink.SinkEventAttributeSet;
33  import org.apache.maven.doxia.sink.SinkEventAttributes;
34  import org.apache.maven.reporting.MavenReportException;
35  
36  /**
37   *
38   */
39  public class SurefireReportGenerator
40  {
41      private final SurefireReportParser report;
42  
43      private List<ReportTestSuite> testSuites;
44  
45      private final boolean showSuccess;
46  
47      private final String xrefLocation;
48  
49      private static final int LEFT = Sink.JUSTIFY_LEFT;
50  
51      public SurefireReportGenerator( List<File> reportsDirectories, Locale locale, boolean showSuccess,
52                                      String xrefLocation )
53      {
54          report = new SurefireReportParser( reportsDirectories, locale );
55  
56          this.xrefLocation = xrefLocation;
57  
58          this.showSuccess = showSuccess;
59      }
60  
61      public void doGenerateReport( ResourceBundle bundle, Sink sink )
62          throws MavenReportException
63      {
64          testSuites = report.parseXMLReportFiles();
65  
66          sink.head();
67  
68          sink.title();
69          sink.text( bundle.getString( "report.surefire.header" ) );
70          sink.title_();
71  
72          sink.head_();
73  
74          sink.body();
75  
76          SinkEventAttributeSet atts = new SinkEventAttributeSet();
77          atts.addAttribute( SinkEventAttributes.TYPE, "text/javascript" );
78          sink.unknown( "script", new Object[]{ HtmlMarkup.TAG_TYPE_START }, atts );
79          sink.unknown( "cdata", new Object[]{ HtmlMarkup.CDATA_TYPE, javascriptToggleDisplayCode() }, null );
80          sink.unknown( "script", new Object[]{ HtmlMarkup.TAG_TYPE_END }, null );
81  
82          sink.section1();
83          sink.sectionTitle1();
84          sink.text( bundle.getString( "report.surefire.header" ) );
85          sink.sectionTitle1_();
86          sink.section1_();
87  
88          constructSummarySection( bundle, sink );
89  
90          Map<String, List<ReportTestSuite>> suitePackages = report.getSuitesGroupByPackage( testSuites );
91          if ( !suitePackages.isEmpty() )
92          {
93              constructPackagesSection( bundle, sink, suitePackages );
94          }
95  
96          if ( !testSuites.isEmpty() )
97          {
98              constructTestCasesSection( bundle, sink );
99          }
100 
101         List<ReportTestCase> failureList = report.getFailureDetails( testSuites );
102         if ( !failureList.isEmpty() )
103         {
104             constructFailureDetails( sink, bundle, failureList );
105         }
106 
107         sink.body_();
108 
109         sink.flush();
110 
111         sink.close();
112     }
113 
114     private void constructSummarySection( ResourceBundle bundle, Sink sink )
115     {
116         Map<String, String> summary = report.getSummary( testSuites );
117 
118         sink.section1();
119         sink.sectionTitle1();
120         sink.text( bundle.getString( "report.surefire.label.summary" ) );
121         sink.sectionTitle1_();
122 
123         sinkAnchor( sink, "Summary" );
124 
125         constructHotLinks( sink, bundle );
126 
127         sinkLineBreak( sink );
128 
129         sink.table();
130 
131         sink.tableRows( new int[]{ LEFT, LEFT, LEFT, LEFT, LEFT, LEFT }, true );
132 
133         sink.tableRow();
134 
135         sinkHeader( sink, bundle.getString( "report.surefire.label.tests" ) );
136 
137         sinkHeader( sink, bundle.getString( "report.surefire.label.errors" ) );
138 
139         sinkHeader( sink, bundle.getString( "report.surefire.label.failures" ) );
140 
141         sinkHeader( sink, bundle.getString( "report.surefire.label.skipped" ) );
142 
143         sinkHeader( sink, bundle.getString( "report.surefire.label.successrate" ) );
144 
145         sinkHeader( sink, bundle.getString( "report.surefire.label.time" ) );
146 
147         sink.tableRow_();
148 
149         sink.tableRow();
150 
151         sinkCell( sink, summary.get( "totalTests" ) );
152 
153         sinkCell( sink, summary.get( "totalErrors" ) );
154 
155         sinkCell( sink, summary.get( "totalFailures" ) );
156 
157         sinkCell( sink, summary.get( "totalSkipped" ) );
158 
159         sinkCell( sink, summary.get( "totalPercentage" ) + "%" );
160 
161         sinkCell( sink, summary.get( "totalElapsedTime" ) );
162 
163         sink.tableRow_();
164 
165         sink.tableRows_();
166 
167         sink.table_();
168 
169         sink.lineBreak();
170 
171         sink.paragraph();
172         sink.text( bundle.getString( "report.surefire.text.note1" ) );
173         sink.paragraph_();
174 
175         sinkLineBreak( sink );
176 
177         sink.section1_();
178     }
179 
180     private void constructPackagesSection( ResourceBundle bundle, Sink sink,
181                                            Map<String, List<ReportTestSuite>> suitePackages )
182     {
183         NumberFormat numberFormat = report.getNumberFormat();
184 
185         sink.section1();
186         sink.sectionTitle1();
187         sink.text( bundle.getString( "report.surefire.label.packagelist" ) );
188         sink.sectionTitle1_();
189 
190         sinkAnchor( sink, "Package_List" );
191 
192         constructHotLinks( sink, bundle );
193 
194         sinkLineBreak( sink );
195 
196         sink.table();
197 
198         sink.tableRows( new int[]{ LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT }, true );
199 
200         sink.tableRow();
201 
202         sinkHeader( sink, bundle.getString( "report.surefire.label.package" ) );
203 
204         sinkHeader( sink, bundle.getString( "report.surefire.label.tests" ) );
205 
206         sinkHeader( sink, bundle.getString( "report.surefire.label.errors" ) );
207 
208         sinkHeader( sink, bundle.getString( "report.surefire.label.failures" ) );
209 
210         sinkHeader( sink, bundle.getString( "report.surefire.label.skipped" ) );
211 
212         sinkHeader( sink, bundle.getString( "report.surefire.label.successrate" ) );
213 
214         sinkHeader( sink, bundle.getString( "report.surefire.label.time" ) );
215 
216         sink.tableRow_();
217 
218         for ( Map.Entry<String, List<ReportTestSuite>> entry : suitePackages.entrySet() )
219         {
220             sink.tableRow();
221 
222             String packageName = entry.getKey();
223 
224             List<ReportTestSuite> testSuiteList = entry.getValue();
225 
226             Map<String, String> packageSummary = report.getSummary( testSuiteList );
227 
228             sinkCellLink( sink, packageName, "#" + packageName );
229 
230             sinkCell( sink, packageSummary.get( "totalTests" ) );
231 
232             sinkCell( sink, packageSummary.get( "totalErrors" ) );
233 
234             sinkCell( sink, packageSummary.get( "totalFailures" ) );
235 
236             sinkCell( sink, packageSummary.get( "totalSkipped" ) );
237 
238             sinkCell( sink, packageSummary.get( "totalPercentage" ) + "%" );
239 
240             sinkCell( sink, packageSummary.get( "totalElapsedTime" ) );
241 
242             sink.tableRow_();
243         }
244 
245         sink.tableRows_();
246 
247         sink.table_();
248 
249         sink.lineBreak();
250 
251         sink.paragraph();
252         sink.text( bundle.getString( "report.surefire.text.note2" ) );
253         sink.paragraph_();
254 
255         for ( Map.Entry<String, List<ReportTestSuite>> entry : suitePackages.entrySet() )
256         {
257             String packageName = entry.getKey();
258 
259             List<ReportTestSuite> testSuiteList = entry.getValue();
260 
261             sink.section2();
262             sink.sectionTitle2();
263             sink.text( packageName );
264             sink.sectionTitle2_();
265 
266             sinkAnchor( sink, packageName );
267 
268             boolean showTable = false;
269 
270             for ( ReportTestSuite suite : testSuiteList )
271             {
272                 if ( showSuccess || suite.getNumberOfErrors() != 0 || suite.getNumberOfFailures() != 0 )
273                 {
274                     showTable = true;
275 
276                     break;
277                 }
278             }
279 
280             if ( showTable )
281             {
282                 sink.table();
283 
284                 sink.tableRows( new int[]{ LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT }, true );
285 
286                 sink.tableRow();
287 
288                 sinkHeader( sink, "" );
289 
290                 sinkHeader( sink, bundle.getString( "report.surefire.label.class" ) );
291 
292                 sinkHeader( sink, bundle.getString( "report.surefire.label.tests" ) );
293 
294                 sinkHeader( sink, bundle.getString( "report.surefire.label.errors" ) );
295 
296                 sinkHeader( sink, bundle.getString( "report.surefire.label.failures" ) );
297 
298                 sinkHeader( sink, bundle.getString( "report.surefire.label.skipped" ) );
299 
300                 sinkHeader( sink, bundle.getString( "report.surefire.label.successrate" ) );
301 
302                 sinkHeader( sink, bundle.getString( "report.surefire.label.time" ) );
303 
304                 sink.tableRow_();
305 
306                 for ( ReportTestSuite suite : testSuiteList )
307                 {
308                     if ( showSuccess || suite.getNumberOfErrors() != 0 || suite.getNumberOfFailures() != 0 )
309                     {
310 
311                         sink.tableRow();
312 
313                         sink.tableCell();
314 
315                         sink.link( "#" + suite.getPackageName() + suite.getName() );
316 
317                         if ( suite.getNumberOfErrors() > 0 )
318                         {
319                             sinkIcon( "error", sink );
320                         }
321                         else if ( suite.getNumberOfFailures() > 0 )
322                         {
323                             sinkIcon( "junit.framework", sink );
324                         }
325                         else if ( suite.getNumberOfSkipped() > 0 )
326                         {
327                             sinkIcon( "skipped", sink );
328                         }
329                         else
330                         {
331                             sinkIcon( "success", sink );
332                         }
333 
334                         sink.link_();
335 
336                         sink.tableCell_();
337 
338                         sinkCellLink( sink, suite.getName(), "#" + suite.getPackageName() + suite.getName() );
339 
340                         sinkCell( sink, Integer.toString( suite.getNumberOfTests() ) );
341 
342                         sinkCell( sink, Integer.toString( suite.getNumberOfErrors() ) );
343 
344                         sinkCell( sink, Integer.toString( suite.getNumberOfFailures() ) );
345 
346                         sinkCell( sink, Integer.toString( suite.getNumberOfSkipped() ) );
347 
348                         String percentage =
349                             report.computePercentage( suite.getNumberOfTests(), suite.getNumberOfErrors(),
350                                                       suite.getNumberOfFailures(), suite.getNumberOfSkipped() );
351                         sinkCell( sink, percentage + "%" );
352 
353                         sinkCell( sink, numberFormat.format( suite.getTimeElapsed() ) );
354 
355                         sink.tableRow_();
356                     }
357                 }
358 
359                 sink.tableRows_();
360 
361                 sink.table_();
362             }
363 
364             sink.section2_();
365         }
366 
367         sinkLineBreak( sink );
368 
369         sink.section1_();
370     }
371 
372     private void constructTestCasesSection( ResourceBundle bundle, Sink sink )
373     {
374         NumberFormat numberFormat = report.getNumberFormat();
375 
376         sink.section1();
377         sink.sectionTitle1();
378         sink.text( bundle.getString( "report.surefire.label.testcases" ) );
379         sink.sectionTitle1_();
380 
381         sinkAnchor( sink, "Test_Cases" );
382 
383         constructHotLinks( sink, bundle );
384 
385         for ( ReportTestSuite suite : testSuites )
386         {
387             List<ReportTestCase> testCases = suite.getTestCases();
388 
389             if ( testCases != null && !testCases.isEmpty() )
390             {
391                 sink.section2();
392                 sink.sectionTitle2();
393                 sink.text( suite.getName() );
394                 sink.sectionTitle2_();
395 
396                 sinkAnchor( sink, suite.getPackageName() + suite.getName() );
397 
398                 boolean showTable = false;
399 
400                 for ( ReportTestCase testCase : testCases )
401                 {
402                     if ( testCase.getFailure() != null || showSuccess )
403                     {
404                         showTable = true;
405 
406                         break;
407                     }
408                 }
409 
410                 if ( showTable )
411                 {
412                     sink.table();
413 
414                     sink.tableRows( new int[]{ LEFT, LEFT, LEFT }, true );
415 
416                     for ( ReportTestCase testCase : testCases )
417                     {
418                         if ( testCase.getFailure() != null || showSuccess )
419                         {
420                             sink.tableRow();
421 
422                             sink.tableCell();
423 
424                             Map<String, Object> failure = testCase.getFailure();
425 
426                             if ( failure != null )
427                             {
428                                 sink.link( "#" + testCase.getFullName() );
429 
430                                 sinkIcon( (String) failure.get( "type" ), sink );
431 
432                                 sink.link_();
433                             }
434                             else
435                             {
436                                 sinkIcon( "success", sink );
437                             }
438 
439                             sink.tableCell_();
440 
441                             if ( failure != null )
442                             {
443                                 sink.tableCell();
444 
445                                 sinkLink( sink, testCase.getName(), "#" + testCase.getFullName() );
446 
447                                 SinkEventAttributeSet atts = new SinkEventAttributeSet();
448                                 atts.addAttribute( SinkEventAttributes.CLASS, "detailToggle" );
449                                 atts.addAttribute( SinkEventAttributes.STYLE, "display:inline" );
450                                 sink.unknown( "div", new Object[]{ HtmlMarkup.TAG_TYPE_START }, atts );
451 
452                                 sink.link( "javascript:toggleDisplay('" + toHtmlId( testCase.getFullName() ) + "');" );
453 
454                                 atts = new SinkEventAttributeSet();
455                                 atts.addAttribute( SinkEventAttributes.STYLE, "display:inline;" );
456                                 atts.addAttribute( SinkEventAttributes.ID, toHtmlId( testCase.getFullName() ) + "off" );
457                                 sink.unknown( "span", new Object[]{ HtmlMarkup.TAG_TYPE_START }, atts );
458                                 sink.text( " + " );
459                                 sink.unknown( "span", new Object[]{ HtmlMarkup.TAG_TYPE_END }, null );
460 
461                                 atts = new SinkEventAttributeSet();
462                                 atts.addAttribute( SinkEventAttributes.STYLE, "display:none;" );
463                                 atts.addAttribute( SinkEventAttributes.ID, toHtmlId( testCase.getFullName() ) + "on" );
464                                 sink.unknown( "span", new Object[]{ HtmlMarkup.TAG_TYPE_START }, atts );
465                                 sink.text( " - " );
466                                 sink.unknown( "span", new Object[]{ HtmlMarkup.TAG_TYPE_END }, null );
467 
468                                 sink.text( "[ Detail ]" );
469                                 sink.link_();
470 
471                                 sink.unknown( "div", new Object[]{ HtmlMarkup.TAG_TYPE_END }, null );
472 
473                                 sink.tableCell_();
474                             }
475                             else
476                             {
477                                 sinkCell( sink, testCase.getName() );
478                             }
479 
480                             sinkCell( sink, numberFormat.format( testCase.getTime() ) );
481 
482                             sink.tableRow_();
483 
484                             if ( failure != null )
485                             {
486                                 sink.tableRow();
487 
488                                 sinkCell( sink, "" );
489                                 sinkCell( sink, (String) failure.get( "message" ) );
490                                 sinkCell( sink, "" );
491                                 sink.tableRow_();
492 
493                                 List<String> detail = (List<String>) failure.get( "detail" );
494                                 if ( detail != null )
495                                 {
496 
497                                     sink.tableRow();
498                                     sinkCell( sink, "" );
499 
500                                     sink.tableCell();
501                                     SinkEventAttributeSet atts = new SinkEventAttributeSet();
502                                     atts.addAttribute( SinkEventAttributes.ID,
503                                                        toHtmlId( testCase.getFullName() ) + "error" );
504                                     atts.addAttribute( SinkEventAttributes.STYLE, "display:none;" );
505                                     sink.unknown( "div", new Object[]{ HtmlMarkup.TAG_TYPE_START }, atts );
506 
507                                     sink.verbatim( null );
508                                     for ( String line : detail )
509                                     {
510                                         sink.text( line );
511                                         sink.lineBreak();
512                                     }
513                                     sink.verbatim_();
514 
515                                     sink.unknown( "div", new Object[]{ HtmlMarkup.TAG_TYPE_END }, null );
516                                     sink.tableCell_();
517 
518                                     sinkCell( sink, "" );
519 
520                                     sink.tableRow_();
521                                 }
522                             }
523                         }
524                     }
525 
526                     sink.tableRows_();
527 
528                     sink.table_();
529                 }
530 
531                 sink.section2_();
532             }
533         }
534 
535         sinkLineBreak( sink );
536 
537         sink.section1_();
538     }
539 
540 
541     private String toHtmlId( String id )
542     {
543         return id.replace( ".", "_" );
544     }
545 
546     private void constructFailureDetails( Sink sink, ResourceBundle bundle, List<ReportTestCase> failureList )
547     {
548         Iterator<ReportTestCase> failIter = failureList.iterator();
549 
550         if ( failIter != null )
551         {
552             sink.section1();
553             sink.sectionTitle1();
554             sink.text( bundle.getString( "report.surefire.label.failuredetails" ) );
555             sink.sectionTitle1_();
556 
557             sinkAnchor( sink, "Failure_Details" );
558 
559             constructHotLinks( sink, bundle );
560 
561             sinkLineBreak( sink );
562 
563             sink.table();
564 
565             sink.tableRows( new int[]{ LEFT, LEFT }, true );
566 
567             while ( failIter.hasNext() )
568             {
569                 ReportTestCase tCase = failIter.next();
570 
571                 Map<String, Object> failure = tCase.getFailure();
572 
573                 sink.tableRow();
574 
575                 sink.tableCell();
576 
577                 String type = (String) failure.get( "type" );
578                 sinkIcon( type, sink );
579 
580                 sink.tableCell_();
581 
582                 sinkCellAnchor( sink, tCase.getName(), tCase.getFullName() );
583 
584                 sink.tableRow_();
585 
586                 String message = (String) failure.get( "message" );
587 
588                 sink.tableRow();
589 
590                 sinkCell( sink, "" );
591 
592                 StringBuilder sb = new StringBuilder();
593                 sb.append( type );
594 
595                 if ( message != null )
596                 {
597                     sb.append( ": " );
598                     sb.append( message );
599                 }
600 
601                 sinkCell( sink, sb.toString() );
602 
603                 sink.tableRow_();
604 
605                 List<String> detail = (List<String>) failure.get( "detail" );
606                 if ( detail != null )
607                 {
608                     boolean firstLine = true;
609 
610                     String techMessage = "";
611                     for ( String line : detail )
612                     {
613                         techMessage = line;
614                         if ( firstLine )
615                         {
616                             firstLine = false;
617                         }
618                         else
619                         {
620                             sink.text( "    " );
621                         }
622                     }
623 
624                     sink.tableRow();
625 
626                     sinkCell( sink, "" );
627 
628                     sink.tableCell();
629                     SinkEventAttributeSet atts = new SinkEventAttributeSet();
630                     atts.addAttribute( SinkEventAttributes.ID, tCase.getName() + "error" );
631                     sink.unknown( "div", new Object[]{ HtmlMarkup.TAG_TYPE_START }, atts );
632 
633                     if ( xrefLocation != null )
634                     {
635                         String path = tCase.getFullClassName().replace( '.', '/' );
636 
637                         sink.link( xrefLocation + "/" + path + ".html#" +
638                                        getErrorLineNumber( tCase.getFullName(), techMessage ) );
639                     }
640                     sink.text(
641                         tCase.getFullClassName() + ":" + getErrorLineNumber( tCase.getFullName(), techMessage ) );
642 
643                     if ( xrefLocation != null )
644                     {
645                         sink.link_();
646                     }
647                     sink.unknown( "div", new Object[]{ HtmlMarkup.TAG_TYPE_END }, null );
648 
649                     sink.tableCell_();
650 
651                     sink.tableRow_();
652                 }
653             }
654 
655             sink.tableRows_();
656 
657             sink.table_();
658         }
659 
660         sinkLineBreak( sink );
661 
662         sink.section1_();
663     }
664 
665     private String getErrorLineNumber( String className, String source )
666     {
667         StringTokenizer tokenizer = new StringTokenizer( source );
668 
669         String lineNo = "";
670 
671         while ( tokenizer.hasMoreTokens() )
672         {
673             String token = tokenizer.nextToken();
674             if ( token.startsWith( className ) )
675             {
676                 int idx = token.indexOf( ":" );
677                 lineNo = token.substring( idx + 1, token.indexOf( ")" ) );
678                 break;
679             }
680         }
681         return lineNo;
682     }
683 
684     private void constructHotLinks( Sink sink, ResourceBundle bundle )
685     {
686         if ( !testSuites.isEmpty() )
687         {
688             sink.paragraph();
689 
690             sink.text( "[" );
691             sinkLink( sink, bundle.getString( "report.surefire.label.summary" ), "#Summary" );
692             sink.text( "]" );
693 
694             sink.text( " [" );
695             sinkLink( sink, bundle.getString( "report.surefire.label.packagelist" ), "#Package_List" );
696             sink.text( "]" );
697 
698             sink.text( " [" );
699             sinkLink( sink, bundle.getString( "report.surefire.label.testcases" ), "#Test_Cases" );
700             sink.text( "]" );
701 
702             sink.paragraph_();
703         }
704     }
705 
706     private void sinkLineBreak( Sink sink )
707     {
708         sink.lineBreak();
709     }
710 
711     private void sinkIcon( String type, Sink sink )
712     {
713         sink.figure();
714 
715         if ( type.startsWith( "junit.framework" ) || "skipped".equals( type ) )
716         {
717             sink.figureGraphics( "images/icon_warning_sml.gif" );
718         }
719         else if ( type.startsWith( "success" ) )
720         {
721             sink.figureGraphics( "images/icon_success_sml.gif" );
722         }
723         else
724         {
725             sink.figureGraphics( "images/icon_error_sml.gif" );
726         }
727 
728         sink.figure_();
729     }
730 
731     private void sinkHeader( Sink sink, String header )
732     {
733         sink.tableHeaderCell();
734         sink.text( header );
735         sink.tableHeaderCell_();
736     }
737 
738     private void sinkCell( Sink sink, String text )
739     {
740         sink.tableCell();
741         sink.text( text );
742         sink.tableCell_();
743     }
744 
745     private void sinkLink( Sink sink, String text, String link )
746     {
747         sink.link( link );
748         sink.text( text );
749         sink.link_();
750     }
751 
752     private void sinkCellLink( Sink sink, String text, String link )
753     {
754         sink.tableCell();
755         sinkLink( sink, text, link );
756         sink.tableCell_();
757     }
758 
759     private void sinkCellAnchor( Sink sink, String text, String anchor )
760     {
761         sink.tableCell();
762         sinkAnchor( sink, anchor );
763         sink.text( text );
764         sink.tableCell_();
765     }
766 
767     private void sinkAnchor( Sink sink, String anchor )
768     {
769         sink.anchor( anchor );
770         sink.anchor_();
771     }
772 
773     private static String javascriptToggleDisplayCode()
774     {
775         final StringBuilder str = new StringBuilder( 64 );
776 
777         // the javascript code is emitted within a commented CDATA section
778         // so we have to start with a newline and comment the CDATA closing in the end
779         str.append( "\n" );
780         str.append( "function toggleDisplay(elementId) {\n" );
781         str.append( " var elm = document.getElementById(elementId + 'error');\n" );
782         str.append( " if (elm && typeof elm.style != \"undefined\") {\n" );
783         str.append( " if (elm.style.display == \"none\") {\n" );
784         str.append( " elm.style.display = \"\";\n" );
785         str.append( " document.getElementById(elementId + 'off').style.display = \"none\";\n" );
786         str.append( " document.getElementById(elementId + 'on').style.display = \"inline\";\n" );
787         str.append( " }" );
788         str.append( " else if (elm.style.display == \"\") {" );
789         str.append( " elm.style.display = \"none\";\n" );
790         str.append( " document.getElementById(elementId + 'off').style.display = \"inline\";\n" );
791         str.append( " document.getElementById(elementId + 'on').style.display = \"none\";\n" );
792         str.append( " } \n" );
793         str.append( " } \n" );
794         str.append( " }\n" );
795         str.append( "//" );
796 
797         return str.toString();
798     }
799 }