helma/modules/tools/Global/helma.Markup.js

807 lines
22 KiB
JavaScript

/*
* Copyright (C) 2004 Hannes Wallnoefer
*/
////////////////////////////////////////////////////////////////////////
// Html element functions
////////////////////////////////////////////////////////////////////////
if (!global.helma) {
global.helma = {};
}
helma.Markup = {};
helma.Markup.element = function(name, attributes, content) {
if (!name) {
throw "helma.Markup.element called without element name";
}
// open tag
res.write("<");
res.write(name);
if (attributes) {
for (var i in attributes) {
if (typeof(attributes[i]) == "undefined")
continue;
res.write(" ");
res.write(i);
res.write("=\"");
res.write(encodeForm(attributes[i]));
res.write("\"");
}
}
// if no child objects create empty element and return
if (typeof(content) == "undefined") {
res.write(" />");
return;
}
res.write(">");
// write content
res.write(content);
// close tag
res.write("</");
res.write(name);
res.write(">");
}
helma.Markup.Element = function(name, attributes, children) {
return new MarkupElement(name, attributes, children);
}
helma.Markup.form = function(attributes, content) {
this.element("form", attributes, content);
}
helma.Markup.Form = function(attributes, children) {
return new MarkupElement("form", attributes, children);
}
helma.Markup.textarea = function(attributes, content) {
this.element("textarea", attributes, encodeForm(content));
}
helma.Markup.Textarea = function(attributes, children) {
return new HtmlTextarea(attributes, children);
}
helma.Markup.input = function(attributes, content) {
this.element("input", attributes, content);
}
helma.Markup.Input = function(attributes, children) {
return new MarkupElement("input", attributes, children);
}
helma.Markup.button = function(attributes, content) {
if (!attributes)
attributes = {};
attributes.type = "button";
this.element("input", attributes, content);
}
helma.Markup.Button = function(attributes, children) {
if (!attributes)
attributes = {};
attributes.type = "button";
return new MarkupElement("input", attributes, children);
}
helma.Markup.submit = function(attributes, content) {
if (!attributes)
attributes = {};
attributes.type = "submit";
this.element("input", attributes, content);
}
helma.Markup.Submit = function(attributes, children) {
if (!attributes)
attributes = {};
attributes.type = "submit";
return new MarkupElement("input", attributes, children);
}
helma.Markup.hidden = function(attributes, content) {
if (!attributes)
attributes = {};
attributes.type = "hidden";
this.element("input", attributes, content);
}
helma.Markup.Hidden = function(attributes, children) {
if (!attributes)
attributes = {};
attributes.type = "hidden";
return new MarkupElement("input", attributes, children);
}
helma.Markup.file = function(attributes, content) {
if (!attributes)
attributes = {};
attributes.type = "file";
this.element("input", attributes, content);
}
helma.Markup.File = function(attributes, children) {
if (!attributes)
attributes = {};
attributes.type = "file";
return new MarkupElement("input", attributes, children);
}
helma.Markup.password = function(attributes, content) {
if (!attributes)
attributes = {};
attributes.type = "password";
this.element("input", attributes, content);
}
helma.Markup.Password = function(attributes, children) {
if (!attributes)
attributes = {};
attributes.type = "password";
return new MarkupElement("input", attributes, children);
}
helma.Markup.checkbox = function(attributes, content) {
if (!attributes)
attributes = {};
attributes.type = "checkbox";
if (!attributes.checked)
delete(attributes.checked);
this.element("input", attributes, content);
}
helma.Markup.Checkbox = function(attributes, children) {
if (!attributes)
attributes = {};
attributes.type = "checkbox";
if (!attributes.checked)
delete(attributes.checked);
return new MarkupElement("input", attributes, children);
}
helma.Markup.select = function(attributes, children, selected, firstLine) {
res.write(new HtmlSelect(attributes, children, selected, firstLine));
}
helma.Markup.Select = function(attributes, children, selected, firstLine) {
return new HtmlSelect(attributes, children, selected, firstLine);
}
helma.Markup.head = function(attributes, content) {
this.element("head", attributes, content);
}
helma.Markup.Head = function(attributes, children) {
return new MarkupElement("head", attributes, children);
}
helma.Markup.title = function(attributes, content) {
this.element("title", attributes, content);
}
helma.Markup.Title = function(attributes, children) {
return new MarkupElement("title", attributes, children);
}
helma.Markup.body = function(attributes, content) {
this.element("body", attributes, content);
}
helma.Markup.Body = function(attributes, children) {
return new MarkupElement("body", attributes, children);
}
helma.Markup.div = function(attributes, content) {
this.element("div", attributes, content);
}
helma.Markup.Div = function(attributes, children) {
return new MarkupElement("div", attributes, children);
}
helma.Markup.p = function(attributes, content) {
this.element("p", attributes, content);
}
helma.Markup.P = function(attributes, children) {
return new MarkupElement("p", attributes, children);
}
helma.Markup.b = function(attributes, content) {
this.element("b", attributes, content);
}
helma.Markup.B = function(attributes, children) {
return new MarkupElement("b", attributes, children);
}
helma.Markup.span = function(attributes, content) {
this.element("span", attributes, content);
}
helma.Markup.Span = function(attributes, children) {
return new MarkupElement("span", attributes, children);
}
helma.Markup.img = function(attributes) {
this.element("img", attributes);
}
helma.Markup.Img = function(attributes) {
return new MarkupElement("img", attributes);
}
helma.Markup.script = function(attributes, content) {
this.element("script", attributes, content);
}
helma.Markup.Script = function(attributes, children) {
return new MarkupElement("script", attributes, children);
}
helma.Markup.ul = function(attributes, content) {
this.element("ul", attributes, content);
}
helma.Markup.Ul = function(attributes, children) {
return new MarkupElement("ul", attributes, children);
}
helma.Markup.ol = function(attributes, content) {
this.element("ol", attributes, content);
}
helma.Markup.Ol = function(attributes, children) {
return new MarkupElement("ol", attributes, children);
}
helma.Markup.li = function(attributes, content) {
this.element("li", attributes, content);
}
helma.Markup.Li = function(attributes, children) {
return new MarkupElement("li", attributes, children);
}
helma.Markup.a = function(attributes, content) {
this.element("a", attributes, content);
}
helma.Markup.link = helma.Markup.a;
helma.Markup.A = function(attributes, children) {
return new MarkupElement("a", attributes, children);
}
helma.Markup.table = function(attributes, content) {
this.element("table", attributes, content);
}
helma.Markup.Table = function(attributes, children) {
return new MarkupElement("table", attributes, children);
}
helma.Markup.Colgroup = function(attributes, children) {
return new MarkupElement("colgroup", attributes, children);
}
helma.Markup.colgroup = function(attributes, content) {
this.element("colgroup", attributes, content);
}
helma.Markup.Col = function(attributes, children) {
return new MarkupElement("col", attributes, children);
}
helma.Markup.col = function(attributes, content) {
this.element("col", attributes, content);
}
helma.Markup.tr = function(attributes, content) {
this.element("tr", attributes, content);
}
helma.Markup.Tr = function(attributes, children) {
return new MarkupElement("tr", attributes, children);
}
helma.Markup.th = function(attributes, content) {
this.element("th", attributes, content);
}
helma.Markup.Th = function(attributes, children) {
return new MarkupElement("th", attributes, children);
}
helma.Markup.td = function(attributes, content) {
this.element("td", attributes, content);
}
helma.Markup.Td = function(attributes, children) {
return new MarkupElement("td", attributes, children);
}
helma.Markup.h1 = function(attributes, content) {
this.element("h1", attributes, content);
}
helma.Markup.H1 = function(attributes, children) {
return new MarkupElement("h1", attributes, children);
}
helma.Markup.h2 = function(attributes, content) {
this.element("h2", attributes, content);
}
helma.Markup.H2 = function(attributes, children) {
return new MarkupElement("h2", attributes, children);
}
helma.Markup.h3 = function(attributes, content) {
this.element("h3", attributes, content);
}
helma.Markup.H3 = function(attributes, children) {
return new MarkupElement("h3", attributes, children);
}
helma.Markup.h4 = function(attributes, content) {
this.element("h4", attributes, content);
}
helma.Markup.H4 = function(attributes, children) {
return new MarkupElement("h4", attributes, children);
}
helma.Markup.h5 = function(attributes, content) {
this.element("h5", attributes, content);
}
helma.Markup.H5 = function(attributes, children) {
return new MarkupElement("h5", attributes, children);
}
helma.Markup.h6 = function(attributes, content) {
this.element("h6", attributes, content);
}
helma.Markup.H6 = function(attributes, children) {
return new MarkupElement("h6", attributes, children);
}
helma.Markup.pre = function(attributes, content) {
this.element("pre", attributes, content);
}
helma.Markup.Pre = function(attributes, children) {
return new MarkupElement("pre", attributes, children);
}
helma.Markup.br = function(attributes) {
this.element("br", attributes);
}
helma.Markup.Br = function(attributes, children) {
return new MarkupElement("br", attributes, children);
}
helma.Markup.openTag = function(name, attributes) {
if (!name) {
throw "helma.Markup.openTag called without element name";
}
res.write("<");
res.write(name);
if (attributes) {
for (var i in attributes) {
if (typeof(attributes[i]) == "undefined")
continue;
res.write(" ");
res.write(i);
res.write("=\"");
res.write(encodeForm(attributes[i]));
res.write("\"");
}
}
res.write(">");
}
helma.Markup.closeTag = function(name) {
res.write("</");
res.write(name);
res.write(">");
}
/**
* utility object to provide an easy way
* for programmatically creating an x/html table.
* @param Number the number of columns in the table
* @param Object the table's, its rows' and cells' attributes
* @return Object an instance of TableWriter
*/
helma.Markup.TableWriter = function(numberOfColumns, attr) {
this.ncols = numberOfColumns;
if (isNaN(this.ncols))
throw "Illegal argument in TableWriter(): first argument must be a number";
if (this.ncols < 1)
throw "Illegal argument in TableWriter(): first argument must be > 1";
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;
this.attr = attr;
// write header row? set to true to use "th" tags for first row
this.writeHeader = false;
// write to string buffer rather than response?
this.writeString = false;
/**
* Write a table cell.
* @param String the table cell content as text
* @param attr an optional attributes holder for the td tag
*/
this.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.Markup.openTag("table", this.attr.table);
helma.Markup.openTag("tr", this.attr.trHead);
} else if (isNewRow) {
helma.Markup.closeTag("tr");
if (isEvenRow)
helma.Markup.openTag("tr", this.attr.trEven);
else
helma.Markup.openTag("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.Markup.openTag(isHeaderRow ? "th" : "td", attr);
// write out table cell contents
if (text)
res.write(text);
// close table cell
helma.Markup.closeTag(isHeaderRow ? "th" : "td");
if (attr && !isNaN(attr.colspan))
this.written += attr.colspan;
else
this.written += 1;
return;
};
/**
* Close all open table tags.
*/
this.close = function() {
if (this.written > 0) {
while (this.written++ % this.ncols != 0)
res.write("<td></td>");
res.write("</tr></table>");
this.written = 0;
}
if (this.writeString)
return res.pop();
return;
};
return this;
}
////////////////////////////////////////////////////////////////////////
// MarkupElement is the base class for all elements
////////////////////////////////////////////////////////////////////////
/**
* Element constructor. Takes a name,
* a map of attributes and an array of child
* elements as arguments.
*/
function MarkupElement(name, attributes, children) {
if (!attributes)
attributes = {};
this.attr = attributes;
// if (name && !this._elementName) {
this._elementName = name;
// } else {
if (attributes && attributes.name) {
this.name = attributes.name;
// this.attr.name = name;
}
this.map = {};
this.add(children);
this.initValueProperty();
}
/**
* Add a new child element
*/
MarkupElement.prototype.add = function(child) {
if (typeof(child) == "undefined") {
return;
}
// initialize child array if necessary
if (!this.children) {
this.children = [];
}
if (child instanceof Array) {
for (var i in child) {
this.add(child[i]);
}
return;
}
// add new child
this.children.push(child);
// register child if it has a name property
if (child) {
if (child.name && !this.map[child.name]) {
this.map[child.name] = child;
}
// register grandchilds unless the name slot is already taken
for (var i in child.map) {
var c = child.map[i];
if (c && c.name && !this.map[c.name]) {
this.map[c.name] = c;
}
}
// set parent property in child
child.parent = this;
}
}
MarkupElement.prototype.firstChild = function() {
return this.children ? this.children[0] : undefined;
}
MarkupElement.prototype.lastChild = function() {
return this.children ? this.children[this.children.length - 1] : undefined;
}
/**
* Render the element to the response buffer.
*/
MarkupElement.prototype.render = function(writer) {
if (!writer)
writer = res;
this.processValueProperty();
// open tag
if (this._elementName) {
writer.write("<");
writer.write(this._elementName);
for (var i in this.attr) {
if (typeof(this.attr[i]) == "undefined")
continue;
writer.write(" ");
writer.write(i);
writer.write("=\"");
writer.write(encodeForm(this.attr[i]));
writer.write("\"");
}
// render type attribute if set
if (this._type) {
writer.write(" type=\"");
writer.write(this._type);
writer.write("\"");
}
// if no child objects create empty element and return
if (typeof(this.children) == "undefined") {
writer.write(" />");
return;
}
writer.write(">");
}
// write child elements
if (typeof(this.children) != "undefined") {
if (this.children instanceof Array) {
for (var i in this.children) {
if (typeof(this.children[i]) instanceof MarkupElement) {
this.children[i].render();
} else if (this.children[i]) {
writer.write(this.children[i]);
}
}
} else {
writer.write(this.children);
}
}
// close tag
if (this._elementName) {
writer.write("</");
writer.write(this._elementName);
writer.write(">");
}
}
/**
* Return an object containing the rendered child elements
* of this element keyed by element name. This is suitable
* for rendering the elements of a markup object through
* a skin, using the object returned by this function as macro
* handler.
*/
MarkupElement.prototype.renderMap = function() {
var map = {};
if (this.children && typeof(this.children) == "object") {
for (var i in this.children) {
if (typeof(this.children[i]) == "object") {
var comp = this.children[i];
map[comp.name] = comp.toString();
}
}
}
return map;
}
/**
* Return an array containing the rendered child elements
* of this element keyed index position. This is suitable
* for those cases where we want to print out a markup
* object's elements programmatically.
*/
MarkupElement.prototype.renderArray = function() {
var list = [];
if (this.children && typeof(this.children) == "object") {
for (var i in this.children) {
if (typeof(this.children[i]) == "object") {
var comp = this.children[i];
list.push(comp.toString());
}
}
}
return list;
}
/**
* Render the element to a string.
*/
MarkupElement.prototype.toString = function() {
res.push();
this.render(res);
return res.pop();
}
/**
* Recursively populate this object and its child objects,
* reading values from the argument object.
*/
MarkupElement.prototype.populate = function(obj) {
// if no object passed populate from req.data
if (!obj)
obj = req.data;
// set value
if (this.name && this._type != "submit")
this.value = obj[this.name];
// populate named child elements
for (var i in this.map) {
if (typeof(this.map[i]) == "object" && this.map[i].populate) {
this.map[i].populate(obj);
}
}
}
/**
* Recursively validate this element and its child elements.
*/
MarkupElement.prototype.validate = function() {
// apply constraints
if (this.constraints) {
for (var i in this.constraints) {
this.contstraints[i].apply(this);
}
}
// validate child elements
for (var i in this.map) {
if (typeof(this.map[i]) == "object" && this.map[i].validate) {
this.map[i].validate();
}
}
}
/**
* Set up this Element's value property.
*/
MarkupElement.prototype.initValueProperty = function() {
this.value = this.attr.value;
}
/**
* Process this Element's value property.
*/
MarkupElement.prototype.processValueProperty = function() {
this.attr.value = this.value;
}
////////////////////////////////////////////////////////////////////////
// MarkupElement subclasses for Html form elements.
////////////////////////////////////////////////////////////////////////
/**
* Html textarea
*/
function HtmlTextarea(attributes, children) {
this.constructor("textarea", attributes, children);
}
HtmlTextarea.prototype = new MarkupElement("textarea");
/**
* Set up this Textarea's value property.
*/
HtmlTextarea.prototype.initValueProperty = function() {
if (typeof(this.attr.value) != "undefined")
this.value = this.attr.value;
else if (this.children && this.children.length > 0)
this.value = this.children[0];
}
/**
* Process this Textarea's value property.
*/
HtmlTextarea.prototype.processValueProperty = function() {
this.children = [encodeForm(this.value)];
}
/**
* Select list
*/
function HtmlSelect(attributes, children, selectedValue, firstLine) {
var options = [];
if (firstLine)
options.push(new MarkupElement("option", {value: ""}, ""));
if (children instanceof Array) {
for (var i in children) {
var child = children[i];
var value, display;
if (child instanceof Array && child.length == 2) {
value = child[0];
display = child[1];
} else if (child.value != null && child.display != null) {
value = child.value;
display = child.display;
} else {
display = child;
value = i;
}
var attr = {value: value};
if (value == selectedValue)
attr.selected = "selected";
options.push(new MarkupElement("option", attr, display));
}
}
this.constructor("select", attributes, options);
}
HtmlSelect.prototype = new MarkupElement("select");