Global.helma.Html ()
Sourcecode in /home/hannes/helma/modules/helma/Html.js:
26:   helma.Html = function() {
27:       var self = this;
28:   
29:       /**
30:        * helper function to render an arbitrary markup part
31:        * @param String the element's name
32:        * @param String prefix of each rendered element
33:        * @param String suffix of each rendered element
34:        * @param Object optional element's attributes
35:       */
36:      var renderMarkupPart = function(name, start, end, attr) {
37:          res.write(start);
38:          res.write(name);
39:          if (attr) {
40:              for (var i in attr) {
41:                  if (i == "prefix" || i == "suffix" || 
42:                      i == "default" || attr[i] == null) {
43:                      continue;
44:                  }
45:                  res.write(" ");
46:                  res.write(i);
47:                  res.write("=\"");
48:                  res.write(attr[i]);
49:                  res.write("\"");
50:              }
51:          }
52:          res.write(end);
53:          return;
54:      };
55:  
56:      /**
57:       * helper function used in Html.checkBox
58:       * and Html.dropDown to check if a current value
59:       * matches against one or more selected values 
60:       * @param String the current value
61:       * @param StringOrArray the selectedValue
62:       */
63:      var isSelected = function(value, selectedValue) {
64:          if (selectedValue == null || value == null)
65:              return false;
66:          if (selectedValue instanceof Array)
67:              return Array.contains(selectedValue, value);
68:          return value == selectedValue;
69:      };
70:  
71:      /**
72:       * return a textual representation of this object
73:       * @return String
74:       */
75:      this.toString = function() {
76:          return "[Antville Html Library]";
77:      };
78:  
79:      /**
80:       * render the opening tag of an arbitrary x/html element
81:       * @param String the element's name
82:       * @param Object the element's attributes
83:       */
84:      this.openTag = function(name, attr) {
85:          renderMarkupPart(name, "<", ">", attr);
86:          return;
87:      };
88:  
89:      /**
90:       * return the opening tag of an arbitrary x/html element
91:       * @see this.openTag
92:       * @return String the rendered element
93:       */
94:      this.openTagAsString = function(name, attr) {
95:          res.push();
96:          renderMarkupPart(name, "<", ">", attr);
97:          return res.pop();
98:      };
99:  
100:      /**
101:       * render the closing tag of an arbitrary x/html element
102:       * directly to response
103:       * @param String the element's name
104:       */
105:      this.closeTag = function(name) {
106:          renderMarkupPart(name, "</", ">", null);
107:          return;
108:      };
109:  
110:      /**
111:       * return the closing tag of an arbitray x/html element
112:       * @see this.closeTag
113:       * @return String the rendered element
114:       */
115:      this.closeTagAsString = function(name) {
116:          res.push();
117:          renderMarkupPart(name, "</", ">", null);
118:          return res.pop();
119:      };
120:  
121:      /**
122:       * render an empty arbitrary x/html element ("contentless tag")
123:       * @param String the element's name
124:       * @param Object the element's attributes
125:      */
126:     this.tag = function(name, attr) {
127:         renderMarkupPart(name, "<", " />", attr);
128:         return;
129:     };
130: 
131:     /**
132:      * return an empty arbitrary x/html element ("contentless tag")
133:      * @see this.tag
134:      * @return String the rendered element
135:      */
136:     this.tagAsString = function(name, attr) {
137:         res.push();
138:         renderMarkupPart(name, "<", " />", attr);
139:         return res.pop();
140:     };
141: 
142:     /**
143:      * render an arbitrary x/html element
144:      * @param String the element's name
145:      * @param String the element's content
146:      * @param Object the element's attributes
147:      */
148:     this.element = function(name, str, attr) {
149:         renderMarkupPart(name, "<", ">", attr);
150:         res.write(str);
151:         renderMarkupPart(name, "</", ">");
152:         return;
153:     };
154: 
155:     /**
156:      * return an arbitrary x/html element
157:      * @see this.element
158:      * @param String the rendered element
159:      */
160:     this.elementAsString = function(name, str, attr) {
161:         res.push();
162:         self.element(name, str, attr);
163:         return res.pop();
164:     };
165: 
166:     /**
167:      * render an x/html link element
168:      * @param Object the element's attributes
169:      * @param String the element's content
170:      */
171:     this.link = function(attr, text) {
172:         if (!attr) {
173:             res.write("[Html.link: insufficient arguments]");
174:             return;
175:         }
176:         self.openTag("a", attr);
177:         res.write(text);
178:         self.closeTag("a");
179:         return;
180:     };
181: 
182:     /**
183:      * return an x/html link element
184:      * @see this.link
185:      * @return String the rendered element
186:      */
187:     this.linkAsString = function(attr, text) {
188:         res.push();
189:         self.link(attr, text);
190:         return res.pop();
191:     };
192: 
193:     /**
194:      * render an x/html hidden input element
195:      * @param Object the element's attributes
196:      */
197:     this.hidden = function(param) {
198:         if (!param) {
199:             res.write("[Html.hidden: insufficient arguments]");
200:             return;
201:         }
202:         var attr = Object.clone(param);
203:         attr.type = "hidden";
204:         attr.value = (attr.value != null) ? encodeForm(attr.value) : "";
205:         self.tag("input", attr);
206:         return;
207:     };
208: 
209:     /**
210:      * return an x/html hidden input element
211:      * @see this.hidden
212:      * @return String the rendered element
213:      */
214:     this.hiddenAsString = function(attr) {
215:         res.push();
216:         self.hidden(attr);
217:         return res.pop();
218:     };
219: 
220:     /**
221:      * render an x/html text input element
222:      * @param Object the element's attributes
223:      */
224:     this.input = function(param) {
225:         if (!param) {
226:             res.write("[Html.input: insufficient arguments]");
227:             return;
228:         }
229:         var attr = Object.clone(param);
230:         attr.type = "text";
231:         if (!attr.size)
232:             attr.size = 20;
233:         attr.value = (attr.value != null) ? encodeForm(attr.value) : "";
234:         self.tag("input", attr);
235:         return;
236:     };
237: 
238:     /**
239:      * return an x/html text input element
240:      * @see this.input
241:      * @return String the rendered element
242:      */
243:     this.inputAsString = function(attr) {
244:         res.push();
245:         self.input(attr);
246:         return res.pop();
247:     };
248: 
249:     /**
250:      * render an x/html textarea element
251:      * @param Object the element's attributes
252:      */
253:     this.textArea = function(param) {
254:         if (!param) {
255:             res.write("[Html.textArea: insufficient arguments]");
256:             return;
257:         }
258:         var attr = Object.clone(param);
259:         var value = (attr.value != null) ? encodeForm(attr.value) : "";
260:         delete attr.value;
261:         self.openTag("textarea", attr);
262:         res.write(value);
263:         self.closeTag("textarea");
264:         return;
265:     };
266: 
267:     /**
268:      * return an x/html textarea element
269:      * @see this.textArea
270:      * @return String the rendered element
271:      */
272:     this.textAreaAsString = function(attr) {
273:         res.push();
274:         self.textArea(attr);
275:         return res.pop();
276:     };
277: 
278:     /**
279:      * render an x/html checkbox input element
280:      * @param Object the element's attributes
281:      */
282:     this.checkBox = function(param) {
283:         if (!param) {
284:             res.write("[Html.checkBox: insufficient arguments]");
285:             return;
286:         }
287:         var attr = Object.clone(param);
288:         attr.type = "checkbox";
289:         if (attr.selectedValue != null) {
290:             if (isSelected(param.value, param.selectedValue))
291:                 attr.checked = "checked";
292:             else
293:                 delete attr.checked;
294:             delete attr.selectedValue;
295:         }
296:         self.tag("input", attr);
297:         return;
298:     };
299: 
300:     /**
301:      * return an x/html checkbox input element
302:      * @see this.checkBox
303:      * @return String the rendered element
304:      */
305:     this.checkBoxAsString = function(attr) {
306:         res.push();
307:         self.checkBox(attr);
308:         return res.pop();
309:     };
310: 
311:     /**
312:      * render an x/html radiobutton input element
313:      * @param Object the element's attributes
314:      */
315:     this.radioButton = function(param) {
316:         if (!param) {
317:             res.write("[Html.radioButton: insufficient arguments]");
318:             return;
319:         }
320:         var attr = Object.clone(param);
321:         attr.type = "radio";
322:         if (attr.selectedValue != null) {
323:             if (attr.value == attr.selectedValue)
324:                 attr.checked = "checked";
325:             else
326:                 delete attr.checked;
327:             delete attr.selectedValue;
328:         }
329:         self.tag("input", attr);
330:         return;
331:     };
332: 
333:     /**
334:      * return an x/html radiobutton input element
335:      * @see this.radioButton
336:      * @return String the rendered element
337:      */
338:     this.radioButtonAsString = function(attr) {
339:         res.push();
340:         self.radioButton(attr);
341:         return res.pop();
342:     };
343: 
344:     /**
345:      * render an x/html submit input element
346:      * @param Object the element's attributes
347:      */
348:     this.submit = function(param) {
349:         if (!param) {
350:             res.write("[Html.submit: insufficient arguments]");
351:             return;
352:         }
353:         var attr = Object.clone(param);
354:         attr.type = "submit";
355:         if (!attr.name)
356:             attr.name = attr.type;
357:         attr.value = (attr.value != null) ? encodeForm(attr.value) : attr.type;
358:         self.tag("input", attr);
359:         return;  
360:     };
361: 
362:     /**
363:      * return an x/html submit input element
364:      * @see this.submit
365:      * @return String the rendered element
366:      */
367:     this.submitAsString = function(attr) {
368:         res.push();
369:         self.submit(attr);
370:         return res.pop();
371:     };
372: 
373:     /**
374:      * render an x/html button input element
375:      * @param Object the element's attributes
376:      */
377:     this.button = function(param) {
378:         if (!param) {
379:             res.write("[Html.button: insufficient arguments]");
380:             return;
381:         }
382:         var attr = Object.clone(param);
383:         attr.type = "button";
384:         if (!attr.name)
385:             attr.name = attr.type;
386:         attr.value = (attr.value != null) ? encodeForm(attr.value) : attr.type;
387:         self.tag("input", attr);
388:         return;  
389:     };
390: 
391:     /**
392:      * return an x/html button input element
393:      * @see this.button
394:      * @return String the rendered element
395:      */
396:     this.buttonAsString = function(attr) {
397:         res.push();
398:         self.button(attr);
399:         return res.pop();
400:     };
401: 
402:     /**
403:      * render a x/html drop down box
404:      * @param Object the element's attributes
405:      * @param Array either an array of strings, an array  with 
406:      *     several {value: v, display: d} objects, or a collection 
407:      *     of ["value", "display"] arrays in an array
408:      * @param String the currently selected value
409:      * @param String the first option (without a value)
410:      */
411:     this.dropDown = function(param, options, selectedValue, firstOption) {
412:         if (!param) {
413:             res.write("[Html.dropDown: insufficient arguments]");
414:             return;
415:         }
416:         var attr = Object.clone(param);
417:         if (!attr.size)
418:             attr.size = 1;
419:         self.openTag("select", attr);
420:         res.write("\n ");
421:         if (firstOption) {
422:             self.openTag("option", {value: ""});
423:             res.write(firstOption);
424:             self.closeTag("option");
425:             res.write("\n ");
426:         }
427:         for (var i in options) {
428:             var attr = new Object();
429:             var display = "";
430:             if ((options[i] instanceof Array) && options[i].length > 0) {
431:                 // option is an array
432:                 attr.value = options[i][0];
433:                 display = options[i][1];
434:             } else if (options[i].value != null && options[i].display != null) {
435:                 // option is an object
436:                 attr.value = options[i].value;
437:                 if (options[i]["class"] != null) {
438:                    attr["class"] = options[i]["class"];
439:                 }
440:                 display = options[i].display;
441:             } else {
442:                 // assume option is a string
443:                 attr.value = i;
444:                 display = options[i];
445:             }
446:             if (isSelected(attr.value, selectedValue))
447:                 attr.selected = "selected";
448:             self.openTag("option", attr);
449:             res.write(display);
450:             self.closeTag("option");
451:             res.write("\n ");
452:         }
453:         self.closeTag("select");
454:         res.write("\n ");
455:         return;
456:     };
457: 
458:     /**
459:      * return an x/html drop down box
460:      * @see this.dropDown
461:      * @return String the rendered drop down box
462:      */
463:     this.dropDownAsString = function(attr, options, selectedValue, firstOption) {
464:         res.push();
465:         self.dropDown(attr, options, selectedValue, firstOption);
466:         return res.pop();
467:     };
468: 
469:     /**
470:      *  Renders an image map from an array
471:      *  each array contains an object describing the parameters
472:      *  for the area of the image map
473:      *  @param String name of the image map
474:      *  @param String array of image map objects
475:      */
476:     this.map = function(name, param) {
477:         self.openTag("map", {name: name});
478:         var areas = Object.clone(param);
479:         for (var i in areas) {
480:             if (!areas[i].alt)
481:                 areas[i].alt = "";
482:             if (!areas[i].shape)
483:                 areas[i].shape = "rect";
484:             self.openTag("area", areas[i]);
485:         }
486:         self.closeTag("map");
487:         return;
488:     };
489: 
490:     /**
491:      * @see Html.map
492:      * @return the rendered element
493:      */
494:     this.mapAsString = function(name, areas) {
495:         res.push();
496:         self.map(name, areas);
497:         return res.pop();
498:     };
499: 
500:     /**
501:      * render a complete x/html table
502:      * @param Array an array containing table headers
503:      * @param Array a 2 dimensional array containing the table data
504:      * @param Object the table's, its rows' and cells' attributes
505:      */
506:     this.table = function(headers, data, param) {
507:         if (!param) {
508:             res.write("[Html.table: insufficient arguments]");
509:             return;
510:         }
511:         var attr = Object.clone(param);
512:         if (!attr.trHead) attr.trHead = attr.tr;
513:         if (!attr.trEven) attr.trEven = attr.tr;
514:         if (!attr.trOdd)  attr.trOdd = attr.tr;
515:         if (!attr.tdEven) attr.tdEven = attr.td;
516:         if (!attr.tdOdd)  attr.tdOdd = attr.td;
517:         if (!attr.thEven) attr.thEven = attr.th;
518:         if (!attr.thOdd)  attr.thOdd = attr.th;
519:         self.openTag("table", attr.table);
520:         if (headers) {
521:             self.openTag("tr", attr.trHead);
522:             for (var i in headers) {
523:                 var evenOdd = i % 2 == 0 ? "Even" : "Odd";
524:                 self.openTag("th", attr["th"+evenOdd]);
525:                 res.write(headers[i]);
526:                 self.closeTag("th");
527:             }
528:             self.closeTag("tr");
529:         }
530:         for (var i in data) {
531:             var evenOdd = i % 2 == 0 ? "Even" : "Odd";
532:             self.openTag("tr", attr["tr"+evenOdd]);
533:             for (var j in data[i]) {
534:                 var evenOddCell = j % 2 == 0 ? "Even" : "Odd";
535:                 self.openTag("td", attr["td"+evenOddCell]);
536:                 res.write(data[i][j]);
537:                 self.closeTag("td");
538:             }
539:             self.closeTag("tr");
540:         }
541:         self.closeTag("table");
542:     };
543: 
544:     /**
545:      * return a complete x/html table
546:      * @see this.table
547:      * @return String the rendered table
548:      */
549:     this.tableAsString = function(headers, data, attr) {
550:         res.push();
551:         self.table(headers, data, attr);
552:         return res.pop();
553:     };
554:     
555:     /**
556:      * utility object to provide an easy way
557:      * for programmatically creating an x/html table.
558:      * @param Number the number of columns in the table
559:      * @param Object the table's, its rows' and cells' attributes
560:      * @return Object an instance of TableWriter
561:      */
562:     this.TableWriter = function(numberOfColumns, attr) {
563:         this.ncols = numberOfColumns;
564:         if (isNaN(this.ncols))
565:             throw "Illegal argument in TableWriter(): first argument must be a number";
566:         if (this.ncols < 1)
567:             throw "Illegal argument in TableWriter(): first argument must be > 1";
568:         this.written = 0;
569:         // if no attributes object given, create an empty one
570:         if (!attr)
571:             attr = {};
572:         if (!attr.trEven) attr.trEven = attr.tr;
573:         if (!attr.trOdd)  attr.trOdd = attr.tr;
574:         if (!attr.trHead) attr.trHead = attr.trEven;
575:         if (!attr.tdEven) attr.tdEven = attr.td;
576:         if (!attr.tdOdd)  attr.tdOdd = attr.td;
577:         if (!attr.thEven) attr.thEven = attr.th;
578:         if (!attr.thOdd)  attr.thOdd = attr.th;
579:         this.attr = attr;
580: 
581:         // write header row? set to true to use "th" tags for first row
582:         this.writeHeader = false;
583:         // write to string buffer rather than response?
584:         this.writeString = false;
585: 
586:         /**
587:          * Write a table cell.
588:          * @param String the table cell content as text
589:          * @param attr an optional attributes holder for the td tag
590:          */
591:         this.write = function(text, attr) {
592:             // set up some variables
593:             var isHeaderRow = (this.writeHeader && this.written < this.ncols);
594:             var isNewRow = (this.written % this.ncols == 0);
595:             var isEvenRow = ((this.written / this.ncols) % 2 == 0);
596:             var isEvenCol = ((this.written % this.ncols) % 2 == 0);
597:             // write out table and table row tags
598:             if (this.written == 0) {
599:                 if (this.writeString)
600:                     res.push();
601:                 self.openTag("table", this.attr.table);
602:                 self.openTag("tr", this.attr.trHead);
603:             } else if (isNewRow) {
604:                 self.closeTag("tr");
605:                 if (isEvenRow)
606:                      self.openTag("tr", this.attr.trEven);
607:                 else
608:                      self.openTag("tr", this.attr.trOdd);
609:             }
610:             // get the attribute object for the table cell
611:             if (!attr) {
612:                 // no explicit attribute given
613:                 if (isEvenCol)
614:                     attr = isHeaderRow ? this.attr.thEven : this.attr.tdEven;
615:                 else
616:                     attr = isHeaderRow ? this.attr.thOdd : this.attr.tdOdd;
617:             }
618:             // write out table cell tag
619:             self.openTag(isHeaderRow ? "th" : "td", attr);
620:             // write out table cell contents
621:             if (text)
622:                 res.write(text);
623:             // close table cell
624:             self.closeTag(isHeaderRow ? "th" : "td");
625:             if (attr && !isNaN(attr.colspan))
626:                 this.written += attr.colspan;
627:             else
628:                 this.written += 1;
629:             return;
630:         };
631: 
632:         /**
633:          * Close all open table tags.
634:          */
635:         this.close = function() {
636:             if (this.written > 0) {
637:                 while (this.written++ % this.ncols != 0)
638:                     res.write("<td></td>");
639:                 res.write("</tr></table>");
640:                 this.written = 0;
641:             }
642:             if (this.writeString)
643:                 return res.pop();
644:             return;
645:         };
646: 
647:         this.dontEnum("ncols", "written", "attr", "writeHeader");
648:         this.dontEnum("writeString", "write", "close");
649: 
650:         return this;
651:     };
652: 
653: /*********************************************************************/
654: /*                                                                                         */
655: /* the following functions should be deliberately altered or removed */
656: /* (most of these can easily be replaced by the methods they call)    */
657: /*                                                                                         */
658: /*********************************************************************/
659: 
660:     /**
661:      * render an x/html open link tag
662:      * @param Object the element's attributes 
663:      */
664:     this.openLink = function(attr) {
665:         self.openTag("a", attr);
666:         return;
667:     };
668: 
669:     /**
670:      * return an x/html open link tag
671:      * @see this.openTag
672:      * @return String the rendered element
673:      */
674:     this.openLinkAsString = function(attr) {
675:         return self.openTagAsString("a", attr);
676:     };
677: 
678:     /**
679:      * render an x/html close link tag
680:      */
681:     this.closeLink = function() {
682:         self.closeTag("a");
683:         return;
684:     };
685: 
686:     /**
687:      * return an x/html close link tag
688:      * @see this.closeLink
689:      * @return String the rendered element
690:      */
691:     this.closeLinkAsString = function() {
692:         return self.closeTagAsString("a");
693:     };
694: 
695:     /**
696:      * render a color string 
697:      * if it contains hex characters only prefix it with "#"
698:      * otherwise assume that it is a named color (eg. "yellow")
699:      * @param String the color string
700:      * @deprecated
701:      */
702:     this.color = function(c) {
703:         if (c) {
704:             var nonhex = /[^0-9,a-f]/gi;
705:             if (!c.match(nonhex)) {
706:                 c = c.pad("0", 6);
707:                 res.write("#");
708:             }
709:         }
710:         res.write(c);
711:         return;
712:     };
713: 
714:     /**
715:      * return a color string
716:      * @see this.color
717:      * @return String the resulting color string
718:      * @deprecated
719:      */
720:     this.colorAsString = function(c) {
721:         res.push();
722:         return self.color(c);
723:         return res.pop();
724:     };
725: 
726:     /**
727:      * render an x/html open form tag
728:      * @param Object the element's attributes
729:      */
730:     this.form = function(attr) {
731:         self.openTag("form", attr);
732:         return;
733:     };
734: 
735:     /**
736:      * return an x/html open form tag
737:      * @see this.form
738:      * @return String the rendered element
739:      */
740:     this.formAsString = function(attr) {
741:         res.push();
742:         self.form(attr);
743:         return res.pop();
744:     };
745: 
746:     /**
747:      * render an x/html password input element
748:      * @param Object the element's attributes
749:      */
750:     this.password = function(attr) {
751:         if (!attr) {
752:             res.write("[Html.password: insufficient arguments]");
753:             return;
754:         }
755:         attr.type = "password";
756:         if (!attr.size)
757:             attr.size = 20;
758:         self.tag("input", attr);
759:         return;
760:     };
761: 
762:     /**
763:      * render an x/html password input element
764:      * @see this.password
765:      * @return String the rendered element
766:      */
767:     this.passwordAsString = function(attr) {
768:         res.push();
769:         self.password(attr);
770:         return res.pop();
771:     };
772: 
773:     /**
774:      * render an x/html file input element
775:      * @param Object the element's attributes
776:      */
777:     this.file = function(attr) {
778:         if (!attr) {
779:             res.write("[Html.file: insufficient arguments]");
780:             return;
781:         }
782:         attr.type = "file";
783:         self.tag("input", attr);
784:         return;
785:     };
786: 
787:     /**
788:      * return an x/html file input element
789:      * @see this.file
790:      * @return String the rendered element
791:      */
792:     this.fileAsString = function(attr) {
793:         res.push();
794:         self.file(attr);
795:         return res.pop();
796:     };
797: 
798:     for (var i in this)
799:         this.dontEnum(i);
800: 
801:     return this;
802: }