/* * Helma License Notice * * The contents of this file are subject to the Helma License * Version 2.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://adele.helma.org/download/helma/license.txt * * Copyright 1998-2006 Helma Software. All Rights Reserved. * * $RCSfile: Html.js,v $ * $Author$ * $Revision$ * $Date$ */ /** * @fileoverview Fields and methods of the helma.Html * and helma.Html.Tablewriter classes. *

* To use this optional module, its repository needs to be added to the * application, for example by calling app.addRepository('modules/helma/Html.js') */ // take care of any dependencies app.addRepository('modules/core/String.js'); app.addRepository('modules/core/Object.js'); app.addRepository('modules/core/Array.js'); /** * Define the global namespace if not existing */ if (!global.helma) { global.helma = {}; } /** * Creates a new instance of helma.Html * @class This class provides various methods for rendering * X/Html tags. * @returns A newly created instance of helma.Html * @constructor */ helma.Html = function() { return this; }; /** * Static helper method that renders an arbitrary markup part. * @param {String} name The element's name * @param {String} start Prefix of each rendered element * @param {String} end Suffix of each rendered element * @param {Object} attr Optional element attributes */ helma.Html.renderMarkupPart = function(name, start, end, attr) { res.write(start); res.write(name); if (attr) { for (var i in attr) { if (i == "prefix" || i == "suffix" || i == "default" || attr[i] == null) { continue; } res.write(" "); res.write(i); res.write("=\""); res.write(attr[i]); res.write("\""); } } res.write(end); return; }; /** * Static helper method used in helma.Html.checkBox * and helma.Html.dropDown to check if a current value * matches against one or more selected values passed * as argument * @param {String} value The current value to check * @param {String|Array} selectedValue Either a single * value to check against the current value, or an array * containing values. * @returns True in case the value is among the selected * values, false otherwise * @type Boolean */ helma.Html.isSelected = function(value, selectedValue) { if (selectedValue == null || value == null) return false; if (selectedValue instanceof Array) return selectedValue.contains(value); return value == selectedValue; }; /** @ignore */ helma.Html.prototype.toString = function() { return "[helma.Html]"; }; /** * Renders the opening tag of an arbitrary x/html tag * @param {String} name The tag name * @param {Object} attr An optional object containing element attributes */ helma.Html.prototype.openTag = function(name, attr) { helma.Html.renderMarkupPart(name, "<", ">", attr); return; }; /** * Returns the opening tag of an arbitrary x/html tag * @param {String} name The tag name * @param {Object} attr An optional object containing element attributes * @returns The rendered x/html opening tag * @type String * @see #openTag */ helma.Html.prototype.openTagAsString = function(name, attr) { res.push(); helma.Html.renderMarkupPart(name, "<", ">", attr); return res.pop(); }; /** * Renders the closing tag of an arbitrary x/html tag * @param {String} name The tag name */ helma.Html.prototype.closeTag = function(name) { helma.Html.renderMarkupPart(name, "", null); return; }; /** * Returns the closing tag of an arbitray x/html element * @param {String} name The tag name * @returns The rendered closing tag * @type String * @see #closeTag */ helma.Html.prototype.closeTagAsString = function(name) { res.push(); helma.Html.renderMarkupPart(name, "", null); return res.pop(); }; /** * Renders an empty arbitrary x/html tag ("contentless tag") * @param {String} name The tag name * @param {Object} attr An optional object containing tag attributes */ helma.Html.prototype.tag = function(name, attr) { helma.Html.renderMarkupPart(name, "<", " />", attr); return; }; /** * Returns an empty arbitrary x/html tag ("contentless tag") * @param {String} name The tag name * @param {Object} attr An optional object containing tag attributes * @returns The rendered element * @type String * @see #tag */ helma.Html.prototype.tagAsString = function(name, attr) { res.push(); helma.Html.renderMarkupPart(name, "<", " />", attr); return res.pop(); }; /** * Renders an arbitrary x/html element * @param {String} name The element name * @param {String} str The content of the element * @param {Object} attr An optional object containing element attributes */ helma.Html.prototype.element = function(name, str, attr) { helma.Html.renderMarkupPart(name, "<", ">", attr); res.write(str); helma.Html.renderMarkupPart(name, ""); return; }; /** * Return an arbitrary x/html element * @param {String} name The element name * @param {String} str The content of the element * @param {Object} attr An optional object containing element attributes * @returns The rendered element * @type String * @see #element */ helma.Html.prototype.elementAsString = function(name, str, attr) { res.push(); this.element(name, str, attr); return res.pop(); }; /** * Renders an x/html link tag * @param {Object} attr An object containing the link attributes * @param {String} text The text to appear as link */ helma.Html.prototype.link = function(attr, text) { if (!attr) { res.write("[Html.link: insufficient arguments]"); return; } this.openTag("a", attr); res.write(text); this.closeTag("a"); return; }; /** * Returns a rendered x/html link tag * @param {Object} attr An object containing the link attributes * @param {String} text The text to appear as link * @returns The rendered link tag * @type String * @see #link */ helma.Html.prototype.linkAsString = function(attr, text) { res.push(); this.link(attr, text); return res.pop(); }; /** * Renders an x/html input tag of type "hidden" * @param {Object} param An object containing the tag attributes */ helma.Html.prototype.hidden = function(param) { if (!param) { res.write("[Html.hidden: insufficient arguments]"); return; } var attr = Object.prototype.reduce.call(param); attr.type = "hidden"; attr.value = (attr.value != null) ? encodeForm(attr.value) : ""; this.tag("input", attr); return; }; /** * Returns a rendered x/html input tag of type "hidden" * @param {Object} attr An object containing the tag attributes * @returns The rendered input element * @type String * @see #hidden */ helma.Html.prototype.hiddenAsString = function(attr) { res.push(); this.hidden(attr); return res.pop(); }; /** * Renders an x/html text input tag * @param {Object} param An object containing the tag attributes */ helma.Html.prototype.input = function(param) { if (!param) { res.write("[Html.input: insufficient arguments]"); return; } var attr = Object.prototype.reduce.call(param); attr.type = param.type || "text"; attr.value = (attr.value != null) ? encodeForm(attr.value) : ""; this.tag("input", attr); return; }; /** * Returns a rendered x/html text input tag * @param {Object} attr An object containing the tag attributes * @returns The rendered text input tag * @type String * @see #input */ helma.Html.prototype.inputAsString = function(attr) { res.push(); this.input(attr); return res.pop(); }; /** * Renders an x/html textarea tag * @param {Object} param An object containing the tag attributes */ helma.Html.prototype.textArea = function(param) { if (!param) { res.write("[Html.textArea: insufficient arguments]"); return; } var attr = Object.prototype.reduce.call(param); var value = (attr.value != null) ? encodeForm(attr.value) : ""; delete attr.value; this.openTag("textarea", attr); res.write(value); this.closeTag("textarea"); return; }; /** * Returns a rendered x/html textarea tag * @param {Object} attr An object containing the tag attributes * @returns The rendered textarea tag * @type String * @see #textArea */ helma.Html.prototype.textAreaAsString = function(attr) { res.push(); this.textArea(attr); return res.pop(); }; /** * Renders an x/html checkbox input tag * @param {Object} param An object containing the tag attributes */ helma.Html.prototype.checkBox = function(param) { if (!param) { res.write("[Html.checkBox: insufficient arguments]"); return; } var attr = Object.prototype.reduce.call(param); attr.type = "checkbox"; if (attr.selectedValue != null) { if (helma.Html.isSelected(param.value, param.selectedValue)) attr.checked = "checked"; else delete attr.checked; delete attr.selectedValue; } this.tag("input", attr); return; }; /** * Returns a rendered x/html checkbox input tag * @param {Object} attr An object containing the tag attributes * @returns The rendered checkbox tag * @type String * @see #checkBox */ helma.Html.prototype.checkBoxAsString = function(attr) { res.push(); this.checkBox(attr); return res.pop(); }; /** * Renders an x/html radiobutton input tag * @param {Object} param An object containing the tag attributes */ helma.Html.prototype.radioButton = function(param) { if (!param) { res.write("[Html.radioButton: insufficient arguments]"); return; } var attr = Object.prototype.reduce.call(param); attr.type = "radio"; if (attr.selectedValue != null) { if (attr.value == attr.selectedValue) attr.checked = "checked"; else delete attr.checked; delete attr.selectedValue; } this.tag("input", attr); return; }; /** * Returns a rendered x/html radio input tag * @param {Object} attr An object containing the tag attributes * @returns The rendered element * @type String * @see #radioButton */ helma.Html.prototype.radioButtonAsString = function(attr) { res.push(); this.radioButton(attr); return res.pop(); }; /** * Renders an x/html submit input tag * @param {Object} param An object containing the tag attributes */ helma.Html.prototype.submit = function(param) { if (!param) { res.write("[Html.submit: insufficient arguments]"); return; } var attr = Object.prototype.reduce.call(param); attr.type = "submit"; if (!attr.name) attr.name = attr.type; attr.value = (attr.value != null) ? encodeForm(attr.value) : attr.type; this.tag("input", attr); return; }; /** * Returns a rendered x/html submit input tag * @param {Object} attr An object containing the tag attributes * @returns The rendered submit input tag * @type String * @see #submit */ helma.Html.prototype.submitAsString = function(attr) { res.push(); this.submit(attr); return res.pop(); }; /** * Renders an x/html button input tag * @param {Object} param An object containing the tag attributes */ helma.Html.prototype.button = function(param) { if (!param) { res.write("[Html.button: insufficient arguments]"); return; } var attr = Object.prototype.reduce.call(param); attr.type = "button"; if (!attr.name) attr.name = attr.type; attr.value = (attr.value != null) ? encodeForm(attr.value) : attr.type; this.tag("input", attr); return; }; /** * Returns a rendered x/html button input tag * @param {Object} param An object containing the tag attributes * @returns The rendered button input tag * @type String * @see #button */ helma.Html.prototype.buttonAsString = function(attr) { res.push(); this.button(attr); return res.pop(); }; /** * Renders a x/html drop down select box * @param {Object} param An object containing the tag attributes * @param {Array} options Either an array of strings, an array with * several {value: v, display: d} objects, or a collection * of ["value", "display"] arrays in an array * @param {String} selectedValue The value to pre-select * @param {String} firstOption An optional first option to display in the * select box (this option will always have no value) */ helma.Html.prototype.dropDown = function(param, options, selectedValue, firstOption) { if (!param) { res.write("[Html.dropDown: insufficient arguments]"); return; } var attr = Object.prototype.reduce.call(param); this.openTag("select", attr); res.write("\n "); if (firstOption) { this.openTag("option", {value: ""}); res.write(firstOption); this.closeTag("option"); res.write("\n "); } var hasOpenGroup = false; for (var i in options) { if (options[i].group) { hasOpenGroup && html.closeTag("optgroup"); html.openTag("optgroup", {label: options[i].group}); hasOpenGroup = true; } var attr = new Object(); var display = ""; if ((options[i] instanceof Array) && options[i].length > 0) { // option is an array attr.value = options[i][0]; display = options[i][1]; } else if (options[i].value != null && options[i].display != null) { // option is an object attr.value = options[i].value; if (options[i]["class"] != null) { attr["class"] = options[i]["class"]; } display = options[i].display; } else { // assume option is a string attr.value = i; display = options[i]; } if (helma.Html.isSelected(attr.value, selectedValue)) attr.selected = "selected"; this.openTag("option", attr); res.write(display); this.closeTag("option"); res.write("\n "); } hasOpenGroup && html.closeTag("optgroup"); this.closeTag("select"); res.write("\n "); return; }; /** * Returns a rendered x/html drop down select box * @param {Object} param An object containing the tag attributes * @param {Array} options Either an array of strings, an array with * several {value: v, display: d} objects, or a collection * of ["value", "display"] arrays in an array * @param {String} selectedValue The value to pre-select * @param {String} firstOption An optional first option to display in the * select box (this option will always have no value) * @returns The rendered drop down select box * @type String * @see #dropDown */ helma.Html.prototype.dropDownAsString = function(attr, options, selectedValue, firstOption) { res.push(); this.dropDown(attr, options, selectedValue, firstOption); return res.pop(); }; /** * Renders an image map based on an array containing the map parameters. * @param {String} name The name of the image map * @param {Array} param An array containing objects, where each of them * contains the attributes for a single image map entry */ helma.Html.prototype.map = function(name, param) { if (!name || !param) { res.write("[Html.map: insufficient arguments]"); return; } this.openTag("map", {name: name}); var attr = Object.prototype.reduce.call(param); for (var i in areas) { if (!areas[i].alt) areas[i].alt = ""; if (!areas[i].shape) areas[i].shape = "rect"; this.openTag("area", areas[i]); } this.closeTag("map"); return; }; /** * Returns a rendered image map based on an array containing the map parameters. * @param {String} name The name of the image map * @param {Array} areas An array containing objects, where each of them * contains the attributes for a single image map entry * @returns The rendered image map * @type String * @see #map */ helma.Html.prototype.mapAsString = function(name, areas) { res.push(); this.map(name, areas); return res.pop(); }; /** * Renders a complete x/html table. * @param {Array} headers An array containing table headers * @param {Array} data A two-dimensional array containing the table data * @param {Object} param An object containing the following properties: * */ helma.Html.prototype.table = function(headers, data, param) { if (!param) { res.write("[Html.table: insufficient arguments]"); return; } var attr = Object.prototype.reduce.call(param); if (!attr.trHead) attr.trHead = attr.tr; if (!attr.trEven) attr.trEven = attr.tr; if (!attr.trOdd) attr.trOdd = attr.tr; if (!attr.tdEven) attr.tdEven = attr.td; if (!attr.tdOdd) attr.tdOdd = attr.td; if (!attr.thEven) attr.thEven = attr.th; if (!attr.thOdd) attr.thOdd = attr.th; this.openTag("table", attr.table); if (headers) { this.openTag("tr", attr.trHead); for (var i in headers) { var evenOdd = i % 2 == 0 ? "Even" : "Odd"; this.openTag("th", attr["th"+evenOdd]); res.write(headers[i]); this.closeTag("th"); } this.closeTag("tr"); } for (var i in data) { var evenOdd = i % 2 == 0 ? "Even" : "Odd"; this.openTag("tr", attr["tr"+evenOdd]); for (var j in data[i]) { var evenOddCell = j % 2 == 0 ? "Even" : "Odd"; this.openTag("td", attr["td"+evenOddCell]); res.write(data[i][j]); this.closeTag("td"); } this.closeTag("tr"); } this.closeTag("table"); return; }; /** * Returns a rendered x/html table * @param {Array} headers An array containing table headers * @param {Array} data A two-dimensional array containing the table data * @param {Object} attr For a description see {@link #table} * @returns The rendered table * @type String * @see #table */ helma.Html.prototype.tableAsString = function(headers, data, attr) { res.push(); this.table(headers, data, attr); return res.pop(); }; /*********************************************************************/ /* */ /* the following functions should be deliberately altered or removed */ /* (most of these can easily be replaced by the methods they call) */ /* */ /*********************************************************************/ /** * Renders an x/html opening link tag * @param {Object} attr An object containing the tag attributes */ helma.Html.prototype.openLink = function(attr) { this.openTag("a", attr); return; }; /** * Returns an x/html opening link tag * @param {Object} attr An object containing the tag attributes * @returns The rendered open link tag * @type String * @see #openTag */ helma.Html.prototype.openLinkAsString = function(attr) { return this.openTagAsString("a", attr); }; /** * Renders an x/html closing link tag */ helma.Html.prototype.closeLink = function() { this.closeTag("a"); return; }; /** * Returns a rendered x/html closing link tag * @returns Rhe rendered closing link tag * @type String * @see #closeLink */ helma.Html.prototype.closeLinkAsString = function() { return this.closeTagAsString("a"); }; /** * Renders a color definition string. If the string passed as * argument contains only hex characters it will be prefixed with a * hash sign if necessary, otherwise this method assumes that the * value is a named color (eg. "yellow"). * @param {String} c The color definintion * @deprecated */ helma.Html.prototype.color = function(c) { if (c) { var nonhex = /[^0-9,a-f]/gi; if (!c.match(nonhex)) { c = c.pad("0", 6); res.write("#"); } } res.write(c); return; }; /** * Returns a color definition. * @param {String} c The color definintion * @returns The rendered color definition * @type String * @see #color * @deprecated */ helma.Html.prototype.colorAsString = function(c) { res.push(); this.color(c); return res.pop(); }; /** * Renders an x/html opening form tag * @param {Object} attr An object containing the tag attributes */ helma.Html.prototype.form = function(attr) { this.openTag("form", attr); return; }; /** * Returns an x/html opening form tag * @param {Object} attr An object containing the tag attributes * @returns The rendered opening form tag * @type String * @see #form */ helma.Html.prototype.formAsString = function(attr) { res.push(); this.form(attr); return res.pop(); }; /** * Renders an x/html password input tag * @param {Object} attr An object containing the tag attributes */ helma.Html.prototype.password = function(attr) { if (!attr) { res.write("[Html.password: insufficient arguments]"); return; } attr.type = "password"; this.tag("input", attr); return; }; /** * Returns a rendered x/html password input tag * @param {Object} attr An object containing the tag attributes * @returns The rendered password input tag * @type String * @see #password */ helma.Html.prototype.passwordAsString = function(attr) { res.push(); this.password(attr); return res.pop(); }; /** * Renders an x/html file input tag * @param {Object} attr An object containing the tag attributes */ helma.Html.prototype.file = function(attr) { if (!attr) { res.write("[Html.file: insufficient arguments]"); return; } attr.type = "file"; this.tag("input", attr); return; }; /** * Returns a rendered x/html file input tag * @param {Object} attr An object containing the tag attributes * @returns The rendered file input tag * @type String * @see #file */ helma.Html.prototype.fileAsString = function(attr) { res.push(); this.file(attr); return res.pop(); }; /** * Parses the string passed as argument and converts any * URL in it into a link tag * @param {String} str The string wherein URLs should be * converted into link tags * @returns The string containing URLs converted into link tags * @type String */ helma.Html.prototype.activateUrls = function(str) { var re = /(^|\/>|\s+)([fhtpsr]+:\/\/[^\s]+?)([\.,;:\)\]\"]?)(?=[\s<]|$)/gim; var func = function(str, p1, p2, p3) { res.push(); res.write(p1); res.write(''); res.write(p2.clip(50, "...", true)); res.write(''); res.write(p3); return res.pop(); }; return str.replace(re, func); }; /** * Creates a new TableWriter instance * @class This class provides various methods for * programmatically creating an x/html table. * @param {Number} numberOfColumns The number of columns in the table * @param {Object} attr An object containing attributes to use when * rendering the single table elements. For a description see {@link #table}. * @returns An instance of TableWriter * @constructor */ helma.Html.TableWriter = function(numberOfColumns, attr) { if (isNaN(numberOfColumns)) throw "Illegal argument in TableWriter(): first argument must be a number"; if (numberOfColumns < 1) throw "Illegal argument in TableWriter(): first argument must be > 1"; /** @private */ this.ncols = numberOfColumns; /** @private */ this.written = 0; // if no attributes object given, create an empty one if (!attr) attr = {}; if (!attr.trEven) attr.trEven = attr.tr; if (!attr.trOdd) attr.trOdd = attr.tr; if (!attr.trHead) attr.trHead = attr.trEven; if (!attr.tdEven) attr.tdEven = attr.td; if (!attr.tdOdd) attr.tdOdd = attr.td; if (!attr.thEven) attr.thEven = attr.th; if (!attr.thOdd) attr.thOdd = attr.th; /** @private */ this.attr = attr; /** * If set to true the first row of the table data is rendered * using <th> tags (defaults to false). * @type Boolean */ this.writeHeader = false; /** * If set to true the TableWriter returns the rendered table * as string, otherwise the table is written directly to response, * which is the default. * @type Boolean */ this.writeString = false; this.dontEnum("ncols", "written", "attr", "writeHeader", "writeString"); return this; }; /** @ignore */ helma.Html.TableWriter.prototype.toString = function() { return "[helma.Html.TableWriter]"; } /** * Writes a single table cell to response. * @param {String} text The content of the table cess * @param {Object} attr An optional object containig attributes * to render for this table cell */ helma.Html.TableWriter.prototype.write = function(text, attr) { // set up some variables var isHeaderRow = (this.writeHeader && this.written < this.ncols); var isNewRow = (this.written % this.ncols == 0); var isEvenRow = ((this.written / this.ncols) % 2 == 0); var isEvenCol = ((this.written % this.ncols) % 2 == 0); // write out table and table row tags if (this.written == 0) { if (this.writeString) res.push(); helma.Html.prototype.openTag.call(this, "table", this.attr.table); helma.Html.prototype.openTag.call(this, "tr", this.attr.trHead); } else if (isNewRow) { helma.Html.prototype.closeTag.call(this, "tr"); if (isEvenRow) helma.Html.prototype.openTag.call(this, "tr", this.attr.trEven); else helma.Html.prototype.openTag.call(this, "tr", this.attr.trOdd); } // get the attribute object for the table cell if (!attr) { // no explicit attribute given if (isEvenCol) { attr = isHeaderRow ? this.attr.thEven : this.attr.tdEven; } else { attr = isHeaderRow ? this.attr.thOdd : this.attr.tdOdd; } } // write out table cell tag helma.Html.prototype.openTag.call(this, isHeaderRow ? "th" : "td", attr); // write out table cell contents if (text) { res.write(text); } // close table cell helma.Html.prototype.closeTag.call(this, isHeaderRow ? "th" : "td"); if (attr && !isNaN(attr.colspan)) { this.written += attr.colspan; } else { this.written += 1; } return; }; /** * Closes all open table tags. If {@link #writeString} is set to * true, this method returns the rendered table. * @returns The rendered table, if {@link #writeString} is set to * true, otherwise void. * @type String */ helma.Html.TableWriter.prototype.close = function() { if (this.written > 0) { while (this.written++ % this.ncols != 0) res.write(""); res.write(""); this.written = 0; } if (this.writeString) return res.pop(); return; }; helma.lib = "Html"; helma.dontEnum(helma.lib); for (var i in helma[helma.lib]) helma[helma.lib].dontEnum(i); for (var i in helma[helma.lib].prototype) helma[helma.lib].prototype.dontEnum(i); for (var i in helma[helma.lib].TableWriter.prototype) helma[helma.lib].TableWriter.prototype.dontEnum(i); delete helma.lib;