/*
* 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:
*
table
: Attributes to render within the opening <table>
tagtr
: Attributes to render within each <tr>
tagtd
: Attributes to render within each <td>
tagth
: Attributes to render within each <th>
tagtrHead
: Attributes to render within each <tr>
tag
in the header area of the tabletrEven
: Attributes to render within each even <tr>
tagtrOdd
: Attributes to render within each odd <tr>
tagtdEven
: Attributes to render within each even <td>
tagtdOdd
: Attributes to render within each odd <td>
tagthEven
: Attributes to render within each even <th>
tagthOdd
: Attributes to render within each odd <th>
tag<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("