Global/Rabbit.js
Summary
Fields and methods of the Rabbit class.
Class Summary
|
Rabbit |
Rabbit is a framework for providing kind of rapid application
prototyping features in Helma. |
app.addRepository("lib/ext/hsqldb.jar");
app.addRepository("modules/core/Global");
app.addRepository("modules/core/String");
var Rabbit = function(restricted) {
var self = this;
var db, meta;
var TYPE = "type.properties";
var METAFILE = app.dir + "/.rabbit";
var INFO = "log";
var log = function(msg, level) {
if (msg) {
var lines = msg.split("\n"), m;
for (var i in lines) {
(m = lines[i]) && app[level || "debug"](m);
}
}
return;
};
var sql = function(s) {
log("Rabbit SQL: " + s);
try {
db.execute(s);
} catch (e) {
log(e.toString());
}
return;
}
var saveMeta = function() {
if (meta) {
serialize(meta, METAFILE);
}
return;
};
var loadMeta = function() {
try {
meta = deserialize(METAFILE);
} catch (e) {
meta = new Object;
}
return;
};
var createTable = function(name) {
var column = getColumnName(name, "id");
sql("create cached table " + getTableName(name) + " (" + column + " " + getSqlType(Rabbit.TEXT) + " not null)");
return column;
};
var addColumn = function(prototype, property, type) {
var column = getColumnName(prototype, property);
sql("alter table " + getTableName(prototype) + " add column " + column + " " + getSqlType(type));
return column;
};
var dropColumn = function(prototype, property) {
sql("alter table " + getTableName(prototype) + " drop column " + getColumnName(prototype, property));
return;
};
var getTableName = function(prototype) {
return "T_" + prototype.toUpperCase();
};
var getColumnName = function(prototype, property) {
return (prototype + "_" + property).toUpperCase();
};
var getSqlType = function(type) {
switch (type) {
case Rabbit.NUMBER:
return "numeric";
case Rabbit.DATE:
return "timestamp";
case Rabbit.FILE:
case Rabbit.IMAGE:
return "binary";
}
return "varchar";
};
var getDir = function(prototype) {
return app.dir + "/" + prototype;
};
var getHeader = function(prefix) {
return prefix + " generated by Rabbit " + Rabbit.VERSION +
" on \n" + prefix + " " + new Date + "\n";
};
var getCollectionName = function(prototype) {
var suffix = "s";
if (prototype.endsWith(suffix)) {
suffix = "e" + suffix;
}
return prototype.toLowerCase() + suffix;
};
var checkProperty = function(property) {
return !property.startsWith("_") &&
!property.startsWith("rabbit") &&
!property.contains(".");
};
var checkValue = function(value) {
return !/object ?\(/.test(value) &&
!/collection ?\(/.test(value) &&
!/mountpoint ?[(]/.test(value);
};
this.init = function() {
loadMeta();
var driver = getProperty("rabbit.driver", "org.hsqldb.jdbcDriver")
java.lang.Class.forName(driver).newInstance();
var dir = new java.io.File(app.dir, "hsql.db/db");
var database = getProperty(
"rabbit.database",
"jdbc:hsqldb:file:" + dir.getCanonicalPath()
);
var user = getProperty("rabbit.user", "sa");
var password = getProperty("rabbit.password", "");
var config = new Object;
config[app.name + ".url"] = database.replace(/\\/g, "/");
config[app.name + ".driver"] = driver;
config[app.name + ".user"] = user;
config[app.name + ".password"] = password;
this.writePropertiesFile(app.dir, "db.properties", config);
var connection = java.sql.DriverManager.getConnection(database, user, password);
db = connection.createStatement();
return db;
};
this.reset = function() {
meta || self.init();
var i, list;
if (arguments.length > 0) {
list = {};
for (i=0; i<arguments.length; i+=1) {
list[arguments[i]] = true;
}
} else {
list = meta;
}
for (i in list) {
sql("drop table " + getTableName(i) + " if exists cascade");
}
meta = {};
saveMeta();
app.clearCache();
return;
};
this.setup = function(prototype, map) {
meta || self.init();
var name = prototype;
if (name.toLowerCase() != "root") {
var value;
var current = self.readPropertiesFile(getDir(name), TYPE);
for (var property in current) {
value = map[property];
if (checkProperty(property) && !value) {
dropColumn(name, property);
}
}
if (!map._db) {
map._db = app.name;
}
if (!map._table) {
map._table = getTableName(name);
}
map._id = createTable(name);
map.rabbit = "mountpoint(XmlMap)";
map.rabbit_xml = addColumn(name, "rabbit_xml", "longvarchar");
}
for (var property in map) {
value = map[property];
if (checkValue(value) && checkProperty(property)) {
self.setMeta(name, property, value);
map[property] = addColumn(name, property, value);
} else if (property.endsWith(".accessname")) {
var collection = property.split(".")[0];
var parts = /collection\s*\(\s*([^)]+)\s*\)/.exec(map[collection]);
var childProto = parts[1];
if (childProto) {
map[property] = getColumnName(childProto, value);
}
}
}
saveMeta();
self.writePropertiesFile(getDir(name), TYPE, map);
return;
};
this.getMeta = function(prototype, property) {
meta || self.init();
var p = meta[prototype];
return p && p[property];
};
this.setMeta = function(prototype, property, value) {
meta || self.init();
var p = meta[prototype];
if (!p) {
meta[prototype] = {};
}
meta[prototype][property] = value;
return;
}
this.getSetup = function(object) {
meta || self.init();
return meta[object._prototype];
};
this.readPropertiesFile = function(dir, name) {
var data = {};
var file = new File(dir, name);
if (file.exists()) {
var content = file.readAll();
var line, pairs;
var parts = content.split("\n");
for (var i in parts) {
line = parts[i].trim();
if (line && !line.startsWith("#")) {
pairs = line.split("=");
data[pairs[0].trim()] = pairs[1].trim();
}
}
}
return data;
};
this.writePropertiesFile = function(dir, name, data) {
var content = new String;
if (data) {
for (var i in data) {
content += i + " = " + data[i] + "\n";
}
var file = new File(dir, name);
file.mkdir();
file.remove();
file.open();
file.writeln(getHeader("##"));
file.write(content);
file.close();
}
return content;
};
this.update = function(object, data) {
var setup = self.getSetup(object);
var property, value, type, fileName, fileType;
for (property in setup) {
value = data[property];
type = self.getMeta(object._prototype, property);
switch (type) {
case Rabbit.DATE:
var i, key, d = {};
for (i in data) {
if (i.startsWith(property + ".")) {
key = i.split(".")[1];
d[key] = data[i];
}
}
value = new Date(d.y, d.M-1, d.d, d.H, d.m, d.s);
break;
case Rabbit.NUMBER:
value = !isNaN(value) ? value : null;
break;
case Rabbit.FILE:
case Rabbit.IMAGE:
if (value && value.content) {
fileName = value.name;
fileType = value.contentType;
value = value.content;
}
break;
}
if ((value = object.onSetProperty(property, value)) !== null) {
object[property] = value;
if (fileType) {
Rabbit.setPropertyMeta(object, property, "type", fileType);
}
if (fileName) {
Rabbit.setPropertyMeta(object, property, "name", fileName);
}
}
}
return;
};
this.editor = function(object) {
Rabbit.snippet("layout:header");
var setup = self.getSetup(object);
var property, value, type;
for (property in setup) {
value = object[property];
type = self.getMeta(object._prototype, property);
switch (type) {
case Rabbit.DATE:
if (value === null) {
value = new Date;
}
value = Rabbit.dateEditor(property, value, new String);
break;
case Rabbit.FILE:
case Rabbit.IMAGE:
value = Rabbit.snippet("html:upload", {
name: property
}, new String);
break;
case Rabbit.NUMBER:
case Rabbit.TEXT:
case Rabbit.EMAIL:
value = Rabbit.snippet("html:textarea", {
type: "text",
name: property,
value: (value === null) ? "" : value
}, new String);
default:
if (type.constructor == Array) {
value = Rabbit.chooser(property, setup[property], value, new String);
}
}
Rabbit.snippet("layout:property", {
name: property.titleize(),
value: value
}, "properties");
}
Rabbit.snippet("layout:editor");
Rabbit.snippet("layout:footer");
return;
};
this.viewer = function(object) {
Rabbit.snippet("layout:header");
var setup = self.getSetup(object);
var property, value, type;
for (property in setup) {
value = object[property];
type = self.getMeta(object._prototype, property);
switch (type) {
case Rabbit.DATE:
value = value.format(Rabbit.DATEFORMAT);
break;
case Rabbit.NUMBER:
value = value.format("0.##");
break;
case Rabbit.FILE:
case Rabbit.IMAGE:
value = Rabbit.snippet("html:link", {
href: object.href("." + property),
text: Rabbit.getPropertyMeta(object, property, "name")
}, new String);
break;
default:
if (value === null) {
value = "";
}
}
Rabbit.snippet("layout:property", {
name: property.titleize(),
value: value
}, "properties");
}
Rabbit.snippet("layout:viewer");
Rabbit.snippet("layout:footer");
return;
};
this.getChildElement = function(child) {
var prototype = global[child];
if (prototype && prototype != Root) {
var target = new prototype;
if (req.data.rabbit) {
self.update(target, req.data);
this.add(target);
res.redirect(target.href());
}
self.editor(target);
return new HopObject;
}
};
this.registerGlobals = function() {
HopObject.prototype.getChildElement = function(obj) {
if (obj.startsWith(" ")) {
return self.getChildElement.call(this, obj.substring(1));
}
if (obj.startsWith(".")) {
return Rabbit.property.call(this, obj.substring(1));
}
return this.get(obj);
};
HopObject.prototype.main_action = function() {
if (this._prototype != "HopObject" && this != root) {
self.viewer(this);
}
return;
};
HopObject.prototype.edit_action = function() {
if (req.data["rabbit"]) {
self.update(this, req.data);
res.redirect(this.href());
}
self.editor(this);
return;
};
global.resetPrototypes = self.reset;
global.registerPrototype = self.setup;
global.render = Rabbit.snippet;
var statics = ["number", "text", "email", "date", "file", "image"];
var i, s;
for (i in statics) {
s = statics[i].toUpperCase();
global[s] = Rabbit[s];
}
return;
};
if (!restricted) {
self.registerGlobals();
}
return this;
};
Rabbit.VERSION = 1.1;
Rabbit.NUMBER = 1;
Rabbit.TEXT = 2;
Rabbit.EMAIL = 3;
Rabbit.DATE = 4;
Rabbit.FILE = 5;
Rabbit.IMAGE = 6;
Rabbit.DATEFORMAT = "d. MMM. yyyy, HH:mm:ss'h'";
Rabbit.snippet = function(name, param, buffer) {
var append = function(str) {
if (buffer.length === 0) {
return str;
}
if (!res.handlers.rendered) {
res.handlers.rendered = new Object;
}
if (!res.handlers.rendered[buffer]) {
res.handlers.rendered[buffer] = "";
}
return res.handlers.rendered[buffer] += str;
};
var DELIMITER = ":";
var prefix = "Rabbit snippet: ";
buffer && res.push();
if (!name.contains(DELIMITER)) {
app.debug("Rabbit skin: " + name);
renderSkin(name, param);
}
var cache;
if (!(cache = res.meta["rabbit:snippets"])) {
cache = res.meta["rabbit:snippets"] = new Object;
}
if (cache[name]) {
app.debug(prefix + name + " (cached)");
renderSkin(cache[name], param);
return buffer && append(res.pop());
}
var NAME = "snippet";
var EXTENSION = "." + NAME + "s";
var PREFIX = "<!" + NAME + DELIMITER;
var parts = name.split(DELIMITER);
var skin = app.skinfiles.Global["rabbit." + parts[0] + EXTENSION];
var snippetName = parts[1];
if (skin) {
if (snippetName) {
var needle = PREFIX + snippetName;
var offset = skin.indexOf(needle);
if (offset < 0) {
return;
}
var start = offset + needle.length + 1;
var end = skin.indexOf(PREFIX, start);
if (end < 0) {
end = skin.length;
}
var snippet = skin.substring(start, end);
if (snippet) {
app.debug(prefix + name);
cache[name] = createSkin(snippet.trim());
renderSkin(cache[name], param);
}
}
}
return buffer && append(res.pop());
};
Rabbit.property = function(property) {
var content = this._prototype && this[property];
if (content) {
res.contentType = Rabbit.getPropertyMeta(this, property, "type");
res.servletResponse.setContentLength(content.length);
var parts = res.contentType && res.contentType.split("/");
if (parts[0].startsWith("application")) {
res.servletResponse.setHeader(
"Content-disposition",
"attachment; filename=" + Rabbit.getPropertyMeta(this, property, "name")
);
}
res.writeBinary(content);
}
return new HopObject;
};
Rabbit.getPropertyMeta = function(object, property, name) {
return object.rabbit.get(property + ":" + name);
};
Rabbit.setPropertyMeta = function(object, property, name, value) {
object.rabbit.set(property + ":" + name, value);
return;
};
Rabbit.chooser = function(name, options, selected, buffer) {
if (typeof options != "object") {
return;
}
res.push();
var value;
for (var i in options) {
value = options[i];
Rabbit.snippet("html:option", {
name: i,
text: value,
selected: (selected && value == selected) ? "selected" : ""
});
}
return Rabbit.snippet("html:chooser", {
name: name,
options: res.pop()
}, buffer);
};
Rabbit.dateEditor = function(name, date, buffer) {
function range(start, stop, step) {
var result = [];
if (!step) {
step = 1;
if (!stop) {
stop = start - 1;
start = 0;
}
}
var max = String(stop).length;
for (var i=start; i<=stop; i+=step) {
result.push(String(i).pad("0", max, String.LEFT));
}
return result;
}
buffer && res.push();
Rabbit.chooser(name + ".d", range(1, 31), date.getDate());
Rabbit.chooser(name + ".M", range(1, 12), date.getMonth() + 1);
Rabbit.chooser(name + ".y", range(1900, 2099), date.getFullYear());
Rabbit.chooser(name + ".H", range(0, 23), date.getHours());
var fifty9 = range(0, 59);
Rabbit.chooser(name + ".m", fifty9, date.getMinutes());
Rabbit.chooser(name + ".s", fifty9, date.getSeconds());
return buffer && res.pop();
};
Documentation generated by
JSDoc on Thu Feb 15 20:58:41 2007