/** * TrimPath Junction. Release 1.0.39. * Copyright (C) 2004, 2005 Metaha. * * TrimPath Junction is licensed under the GNU General Public License * and the Apache License, Version 2.0, as follows: * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var TrimPath; (function(safeEval) { if (TrimPath == null) TrimPath = new Object(); var MANY_ZEROS = "000000000000000000"; var junctionUtil = TrimPath.junctionUtil = { safeEval : safeEval, upperFirst : function(str) { return str.charAt(0).toUpperCase() + str.substring(1); }, lowerFirst : function(str) { return str.charAt(0).toLowerCase() + str.substring(1); }, encodeAngles : function(str) { return str.replace(//g, '>'); }, decodeAngles : function(str) { return str.replace(/</g, '<').replace(/>/g, '>'); }, encodeQuotes : function(str) { // Useful for inline javascript event handler attributes. return str.replace(/'/g, "\\'").replace(/"/g, '"'); }, leftZeroPad : function(val, minLength) { if (typeof(val) != "string") val = String(val); return (MANY_ZEROS.substring(0, minLength - val.length)) + val; }, toLocalDateString : function(date, withTime) { if (typeof(date) == "string") { date = junctionUtil.parseDateString(date); if (date == null) return ""; } date = date || new Date(); var result = junctionUtil.leftZeroPad(date.getFullYear(), 4) + '/' + junctionUtil.leftZeroPad(date.getMonth() + 1, 2) + '/' + junctionUtil.leftZeroPad(date.getDate(), 2); if (withTime == true) result = result + ' ' + junctionUtil.leftZeroPad(date.getHours(), 2) + ':' + junctionUtil.leftZeroPad(date.getMinutes(), 2) + ':' + junctionUtil.leftZeroPad(date.getSeconds(), 2); return result; }, toSQLDateString : function(date) { date = date || new Date(); return junctionUtil.leftZeroPad(date.getUTCFullYear(), 4) + '/' + junctionUtil.leftZeroPad(date.getUTCMonth() + 1, 2) + '/' + junctionUtil.leftZeroPad(date.getUTCDate(), 2) + ' ' + junctionUtil.leftZeroPad(date.getUTCHours(), 2) + ':' + junctionUtil.leftZeroPad(date.getUTCMinutes(), 2) + ':' + junctionUtil.leftZeroPad(date.getUTCSeconds(), 2) + ' UTC'; }, prepSQLParams : function(sqlParams) { if (sqlParams != null) { for (var i = 0; i < sqlParams.length; i++) { if (sqlParams[i] instanceof Date) sqlParams[i] = junctionUtil.toSQLDateString(sqlParams[i]); } } return sqlParams; }, isValidSQLName : function(name) { return (name.search(/^\w+$/) == 0); }, parseDateString : function(s) { var d = new Date(s); if (d != null && isNaN(d.getTime()) == false) return d; return null; }, setMapTreeValue : function(mapTree, path, value) { // Example path is 'order[customer][name]'. if (path != null) { var keys = path.replace(/\]/g, '').split('['); for (var k = 0; k < keys.length; k++) { var key = keys[k]; if (k < keys.length - 1) { if (mapTree[key] == null) mapTree[key] = {}; mapTree = mapTree[key]; } else { mapTree[key] = value; } } } }, copyProps : function(src, dst) { // The dst is always a record-like object. for (var k in src) { if (typeof(src[k]) != 'function' && junctionUtil.isValidSQLName(k)) { dst[k] = src[k]; // By convention, fields with "Id" suffix are integers. var suffix = k.slice(-2); if (suffix == "Id") dst[k] = junctionUtil.nanToNull(parseInt(dst[k])); // By convention, fields with "At" suffix are dates. if (suffix == "At" && src[k] != null) { if (typeof(src[k]) == "string") { var d = junctionUtil.parseDateString(src[k]); if (d != null) dst[k] = junctionUtil.toSQLDateString(d); else dst[k] = null; } if (src[k] instanceof Date) dst[k] = junctionUtil.toSQLDateString(src[k]); } } } return dst; }, nanToNull : function(val) { return isNaN(val) ? null : val; }, findRecordIndex : function(records, id) { // Returns the index of the record with the given id. if (records != null) { for (var i = 0; i < records.length; i++) { if (records[i].id == id) return i; } } return -1; }, beforeFilter : function(funcFilter, scopeObj) { // The remaining varargs are action function names in the scope object. var installBeforeFilter = function(actionName) { var funcOrig = scopeObj[actionName]; scopeObj[actionName] = function(req, res) { if (funcFilter(req, res) == false) return; return funcOrig(req, res); } } for (var i = 2; i < arguments.length; i++) installBeforeFilter(arguments[i]); }, callIfExists : function(obj, methodName, defaultResult) { if (obj[methodName] == null) return defaultResult; return obj[methodName](); }, createMemoryDataProvider : function(data, idGenerator) { var recordChanged = function(tableName, id, op) { var changes = memoryDataProvider.changesFor(tableName); changes[id] = [ op ]; } var memoryDataProvider = { changesFor : function(tableName) { var key = "changes @@ " + tableName; if (data[key] == null) // We store changes into the data hash so that changes are data[key] = {}; // auto-serialized to json along with any normal data. return data[key]; }, query : function(stmt) { return stmt.filter(data); // See TrimQuery API. }, save : function(tableName, obj) { var records = data[tableName]; if (records == null) records = data[tableName] = []; var index = junctionUtil.findRecordIndex(records, obj.id); if (index >= 0) { junctionUtil.copyProps(obj, records[index]); recordChanged(tableName, obj.id, "update"); return true; } if (obj.isNewRecord()) // We use a negative id value to denote client-only existence. obj.id = idGenerator.nextId(); records.push(junctionUtil.copyProps(obj, {})); recordChanged(tableName, obj.id, "insert"); return true; }, destroy : function(tableName, id) { var records = data[tableName]; var index = junctionUtil.findRecordIndex(records, id); if (index >= 0) { records.splice(index, 1); recordChanged(tableName, id, "delete"); return true; } return false; }, resetAll : function() { for (var tableName in data) { if (typeof(data[tableName]) != 'function') data[tableName] = (junctionUtil.isValidSQLName(tableName) ? [] : {}); } if (TrimPath.junction.env.syncDOMData != null) TrimPath.junction.env.syncDOMData(); }, // Attributes that are part of memory data provider only... memoryData : data // Keyed by table name. }; return memoryDataProvider; }, toArray : function(obj, length, filterFunc) { length = length || obj.length; var result = []; for (var i = 0; i < length; i++) if (filterFunc == null || filterFunc(obj[i])) result.push(obj[i]); return result; }, pushAttributes : function(array, attrs) { for (var k in attrs) if (typeof(attrs[k]) != "function") array.push(' ' + k + '="' + attrs[k] + '"'); return array; } }; var ModelErrors = function() { this.clear(); }; ModelErrors.prototype.clear = function() { this.attrErrorsCount = 0; this.attrErrors = {}; } ModelErrors.prototype.add = function(attrName, msg) { if (this.attrErrors[attrName] == null) this.attrErrors[attrName] = []; this.attrErrors[attrName].push(msg || "is invalid"); this.attrErrorsCount++; } ModelErrors.prototype.addToBase = function(msg) { this.add(":base", msg); } ModelErrors.prototype.onBase = function() { return this.on(":base"); } ModelErrors.prototype.on = function(attrName) { return this.attrErrors[attrName]; } ModelErrors.prototype.isInvalid = function(attrName) { return this.on(attrName) != null; } ModelErrors.prototype.count = function() { return this.attrErrorsCount; } ModelErrors.prototype.isEmpty = function() { return this.count() == 0; } ModelErrors.prototype.invalidAttributes = function() { var result = []; for (var attrName in this.attrErrors) { var errs = this.attrErrors[attrName]; if (errs != null && errs instanceof Array) result.push(attrName); } return result; } ModelErrors.prototype.fullMessagesOn = function(attrName) { var msgs = this.on(attrName); if (msgs == null || attrName == ":base") return msgs; var results = []; for (var i = 0; i < msgs.length; i++) results.push(attrName + " " + msgs[i]); return results; } ModelErrors.prototype.fullMessages = function() { var results = []; for (var i = 0, attrs = this.invalidAttributes(); i < attrs.length; i++) results.concat(this.fullMessagesOn(attrs[i]) || []) return results; } TrimPath.junctionCreate = function(env) { var modelInfos = {}; // Keyed by model name, like 'InvoiceLine'. var modelQueryLang = null; var lastFormArgs = null; // For startFormTag() and the other form tag helpers. var getFunc_cache = {}; var getFunc = function(funcName) { try { return safeEval(funcName); } catch (e) {}; return null; // No actual caching, because it prevents in-browser app development. // if (getFunc_cache[funcName] == null) // getFunc_cache[funcName] = safeEval(funcName); // return getFunc_cache[funcName]; } var sqlPrepare_cache = {}; var sqlPrepare = function(sql, sqlParams) { var key = sql + " -- " + sqlParams; if (sqlPrepare_cache[key] == null) { sqlPrepare_value = junction.queryLang().parseSQL(sql, sqlParams); sqlPrepare_cache[key] = sqlPrepare_value; // Note, do not collapse into previous line of code } // because of conflicts with junction.queryLang(forceReset == true). return sqlPrepare_cache[key]; } var conditionsPrep = function(conditions) { if (conditions == null) return ""; if (conditions.slice(0, 9) == "ORDER BY ") return " " + conditions; return " WHERE " + conditions; } var junction = { env : env, queryLang : function(forceReset) { if (modelQueryLang == null || forceReset == true) { modelQueryLang = env.makeQueryLang(env.dataSchemaProvider.getSchema()); sqlPrepare_cache = {}; } return modelQueryLang; }, localSqlExecute : function(sql, optParams) { return env.dataProvider.query(sqlPrepare(sql, junctionUtil.prepSQLParams(optParams))); }, modelFor : function(modelName, func) { // The modelName is like 'InvoiceLine'. var modelInfo = modelInfos[modelName]; if (modelInfo == null || modelInfo.func != getFunc(modelName)) { modelInfo = modelInfos[modelName] = { name : modelName, funcName : modelName, func : func || getFunc(modelName), tableName : modelName, pluralName : modelName + 's', hasOne : {}, hasMany : {}, belongsTo : {}, validations : { save : [], create : [], update : [] } } modelInfo.func.findAll = function(conditions, optParams) { var sql = "SELECT " + modelInfo.tableName + ".* FROM " + modelInfo.tableName + conditionsPrep(conditions); return modelInfo.func.findBySql(sql, optParams); } modelInfo.func.find = function(id) { return modelInfo.func.findFirst(modelInfo.tableName + ".id = " + parseInt(id)); // TODO: SQL injection problem here. } modelInfo.func.findFirst = function(conditions, optParams) { var sql = "SELECT " + modelInfo.tableName + ".* FROM " + modelInfo.tableName + conditionsPrep(conditions); var record = env.dataProvider.query(sqlPrepare(sql, junctionUtil.prepSQLParams(optParams)))[0]; if (record != null) return modelInfo.func.newInstance(record); return null; } modelInfo.func.findBySql = function(sql, optParams) { var records = junction.localSqlExecute(sql, optParams); var result = []; for (var i = 0; i < records.length; i++) result.push(modelInfo.func.newInstance(records[i])); return result; } modelInfo.func.newInstance = function(attrs) { var newObj = junctionUtil.copyProps(attrs, new (modelInfo.func)()); if (newObj != null) newObj.setConventionalAttributes(true); return newObj; } modelInfo.func.prototype.destroy = function() { junctionUtil.callIfExists(this, "beforeDestroy"); return env.dataProvider.destroy(modelInfo.tableName, this.id); } modelInfo.func.prototype.save = function() { var suffix = this.isNewRecord() ? "Create" : "Update"; // Need to capture this before state changes. if (this.isValid() == false) return false; this.setConventionalAttributes(); junctionUtil.callIfExists(this, "beforeSave"); junctionUtil.callIfExists(this, "before" + suffix); if (env.dataProvider.save(modelInfo.tableName, this) == false) return false; junctionUtil.callIfExists(this, "after" + suffix); junctionUtil.callIfExists(this, "afterSave"); return true; } modelInfo.func.prototype.isValid = function() { this.errors().clear(); var suffix = this.isNewRecord() ? "OnCreate" : "OnUpdate"; junctionUtil.callIfExists(this, "beforeValidation"); junctionUtil.callIfExists(this, "beforeValidation" + suffix); this.runValidations("save"); this.runValidations(this.isNewRecord() ? "create" : "update"); junctionUtil.callIfExists(this, "validate"); junctionUtil.callIfExists(this, "validate" + suffix); junctionUtil.callIfExists(this, "afterValidation"); junctionUtil.callIfExists(this, "afterValidation" + suffix); return this.errors().isEmpty(); } modelInfo.func.prototype.runValidations = function(type) { var funcs = modelInfo.validations[type]; for (var i = 0; i < funcs.length; i++) funcs[i](this); } modelInfo.func.prototype.isNewRecord = function() { return this.id == null || this.id == 0; } modelInfo.func.prototype.errors = function() { if (this["@errors"] == null) this["@errors"] = new ModelErrors(); return this["@errors"]; } modelInfo.func.prototype.updateAttributes = function(attrs) { var id = this.id; junctionUtil.copyProps(attrs, this); this.id = id; return this.save(); } modelInfo.func.prototype.setConventionalAttributes = function(skipLockVersion) { var tableSchema = env.dataSchemaProvider.getSchema()[modelInfo.tableName]; if (tableSchema != null) { if (tableSchema['lockVersion']) { if (this.lockVersion == null) this.lockVersion = 0; if (skipLockVersion != true) // Treats null as false. this.lockVersion += 1; } var now; if (tableSchema['createdAt'] && this.createdAt == null) this.createdAt = now = (now || junctionUtil.toSQLDateString(new Date())); if (tableSchema['createdOn'] && this.createdOn == null) this.createdOn = now = (now || junctionUtil.toSQLDateString(new Date())); if (tableSchema['updatedAt']) this.updatedAt = now = (now || junctionUtil.toSQLDateString(new Date())); if (tableSchema['updatedOn']) this.updatedOn = now = (now || junctionUtil.toSQLDateString(new Date())); } } modelInfo.metaAspects = { tableName : function(tableNameVal) { modelInfo.tableName = tableNameVal; }, pluralName : function(pluralNameVal) { modelInfo.pluralName = pluralNameVal; }, hasMany : function(childPluralName, info) { // The childPluralName is like 'TodoItems'. info = info || {}; info.modelName = info.modelName || childPluralName.slice(0, -1); info.foreignKey = info.foreignKey || junctionUtil.lowerFirst(modelName) + "Id"; modelInfo.hasMany[childPluralName] = info; modelInfo.func.prototype['get' + childPluralName] = function(forceReload) { if (forceReload == true || this['_cached_' + childPluralName] == null) { var childModelInfo = modelInfos[info.modelName]; var conditions = childModelInfo.tableName + "." + info.foreignKey + " = " + this.id; if (info.conditions) { if (info.conditions.indexOf("ORDER BY ") != 0) conditions += " AND"; conditions += " " + info.conditions; } this['_cached_' + childPluralName] = childModelInfo.func.findAll(conditions); } return this['_cached_' + childPluralName]; } }, belongsTo : function(parentName, info) { // The parentName is like 'TodoList'. info = info || {}; info.modelName = info.modelName || parentName; info.foreignKey = info.foreignKey || junctionUtil.lowerFirst(parentName) + "Id"; modelInfo.belongsTo[parentName] = info; modelInfo.func.prototype['get' + parentName] = function(forceReload) { if (forceReload == true || this['_cached_' + parentName] == null) { var parentModelInfo = modelInfos[info.modelName]; var conditions = parentModelInfo.tableName + ".id = " + this[info.foreignKey]; this['_cached_' + parentName] = parentModelInfo.func.findFirst(conditions); } return this['_cached_' + parentName]; } }, validatesFormatOf : function(attrName, regexp, msg, on) { modelInfo.validations[on || 'save'].push(function(obj) { if (obj[attrName] != null && String(obj[attrName]).match(regexp) == null) obj.errors().add(attrName, msg || "is invalid"); }); }, validatesInclusionOf : function(attrName, inArray, msg, on) { modelInfo.validations[on || 'save'].push(function(obj) { var val = obj[attrName]; if (val == null) return; for (var i = 0; i < inArray.length; i++) if (val == inArray[i]) return; obj.errors().add(attrName, msg || "is not included in the list"); }); }, validatesPresenceOf : function(attrName, msg, on) { modelInfo.validations[on || 'save'].push(function(obj) { if (obj[attrName] == null || obj[attrName] == "") obj.errors().add(attrName, msg || "can't be empty"); }); }, toString : function() { var result = '{'; for (var k in modelInfo) result = result + k + ': ' + modelInfo(k) + ', '; return result + '}'; } } } return modelInfo.metaAspects; }, scaffold : function(controllerFunc, modelName) { // The modelName is like 'InvoiceLine'. controllerFunc.prototype.index = function(req, res) { var modelInfo = modelInfos[modelName]; res[junctionUtil.lowerFirst(modelInfo.pluralName)] = modelInfo.func.findAll(); } controllerFunc.prototype.show = controllerFunc.prototype.edit = function(req, res) { var modelInfo = modelInfos[modelName]; res[junctionUtil.lowerFirst(modelInfo.name)] = modelInfo.func.find(req['objId']); } controllerFunc.prototype.update = function(req, res) { var modelInfo = modelInfos[modelName]; var key = junctionUtil.lowerFirst(modelInfo.name); res[key] = modelInfo.func.find(req['objId']); if (res[key].updateAttributes(req[key])) { res.flash['notice'] = 'The ' + modelName + ' is updated.'; res.redirectToAction('show', res[key].id); } else res.renderAction('edit'); } controllerFunc.prototype.newInstance = function(req, res) { var modelInfo = modelInfos[modelName]; res[junctionUtil.lowerFirst(modelInfo.name)] = modelInfo.func.newInstance(); } controllerFunc.prototype.create = function(req, res) { var modelInfo = modelInfos[modelName]; var key = junctionUtil.lowerFirst(modelInfo.name); res[key] = modelInfo.func.newInstance(req[key]); if (res[key].save()) { res.flash['notice'] = 'The ' + modelName + ' is created.'; res.redirectToAction('show', res[key].id); } else res.renderAction('newInstance'); } controllerFunc.prototype.destroy = function(req, res) { var modelInfo = modelInfos[modelName]; var obj = modelInfo.func.find(req['objId']); if (obj != null) { obj.destroy(); res.flash['notice'] = 'The ' + modelName + ' is deleted.'; } else res.flash['notice'] = 'We could not delete an unknown ' + modelName + '.'; res.redirectToAction('index'); } }, /////////////////////////////////////////////// localGet : function(controllerName, actionName, objId, req) { return junction.localRequest("get", controllerName, actionName, objId, req); }, localPost : function(controllerName, actionName, objId, req) { return junction.localRequest("post", controllerName, actionName, objId, req); }, localRequest : function(type, controllerName, actionName, objIdOrig, req) { var controllerFuncName = junctionUtil.upperFirst(controllerName) + "Controller"; var controllerFunc = getFunc(controllerFuncName); if (controllerFunc == null || typeof(controllerFunc) != 'function') return env.errorUnknownController(controllerName, controllerFuncName); var objId = env.mapObjId(objIdOrig); if (req == null) req = {}; req.type = type; req.controllerName = controllerName; req.actionName = actionName; req.objId = objId; req.session = env.getSession(); var urlForArgsCheck = function(controllerNameVal, actionNameVal, objIdVal) { return { controllerNameVal : (controllerNameVal || controllerName), actionNameVal : (actionNameVal || actionName), objIdVal : (objIdVal) }; } var resRendered = null; var resRedirect = null; var res = { req : req, session : req.session, flash : req.session.flash || {}, layoutName : controllerFunc.layoutName || 'default', renderAction : function(actionName) { return res.render(controllerName + '/' + actionName); }, render : function(templateName) { if (templateName == null) templateName = controllerName + '/' + actionName; return res.renderTemplate("/app/views/" + templateName + ".jst"); }, renderTemplate : function(templatePath) { return res.renderText(env.templateRenderer(templatePath, res)); }, renderText : function(text) { resRendered = text; return text; }, redirectTo : function(controllerNameVal, actionNameVal, objIdVal) { resRedirect = urlForArgsCheck(controllerNameVal, actionNameVal, objIdVal); }, redirectToAction : function(actionNameVal, objIdVal) { res.redirectTo(controllerName, actionNameVal, objIdVal); }, urlForArgs : function(args) { var result = [ '?controllerName=', args.controllerNameVal, '&actionName=', args.actionNameVal ]; if (args.objIdVal) result.push('&objId=' + args.objIdVal); return result.join(''); }, urlFor : function(controllerNameVal, actionNameVal, objIdVal) { return res.urlForArgs(urlForArgsCheck(controllerNameVal, actionNameVal, objIdVal)); }, htmlOptionsPrepareConfirm : function(htmlOptions) { if (htmlOptions != null && htmlOptions.confirm != null) { if (htmlOptions.confirm == true) htmlOptions.confirm = "Are you sure?"; htmlOptions.onclick = "if (!confirm('" + htmlOptions.confirm + "')) return false;" + (htmlOptions.onclick || ""); htmlOptions.confirm = null; if (htmlOptions['class'] == null) htmlOptions['class'] = 'confirm'; } }, linkToArgs : function(linkText, args, htmlOptions, asButton) { res.htmlOptionsPrepareConfirm(htmlOptions); var result; if (asButton != true) result = [ ''); if (asButton != true) { result.push(linkText); result.push(''); } return result.join(''); }, linkTo : function(linkText, controllerNameVal, actionNameVal, objIdVal, htmlOptions) { return res.linkToArgs(linkText, urlForArgsCheck(controllerNameVal, actionNameVal, objIdVal), htmlOptions); }, linkToLocal : function(linkText, controllerNameVal, actionNameVal, objIdVal, htmlOptions, asButton) { var args = urlForArgsCheck(controllerNameVal, actionNameVal, objIdVal); htmlOptions = htmlOptions || {}; htmlOptions.onclick = htmlOptions.onclick || ""; htmlOptions.onclick = htmlOptions.onclick + ";TrimPath.junction.localGet('" + args.controllerNameVal + "', '" + args.actionNameVal + "'"; if (args.objIdVal) htmlOptions.onclick += ", '" + args.objIdVal + "'"; htmlOptions.onclick += "); return false"; return res.linkToArgs(linkText, args, htmlOptions, asButton); }, buttonToLocal : function(buttonText, controllerNameVal, actionNameVal, objIdVal, htmlOptions) { return res.linkToLocal(buttonText, controllerNameVal, actionNameVal, objIdVal, htmlOptions, true); }, defaultFormId : function(args) { return res.urlForArgs(args).replace(/&/g, '|'); }, startFormTag : function(controllerNameVal, actionNameVal, objIdVal, htmlOptions) { lastFormArgs = urlForArgsCheck(controllerNameVal, actionNameVal, objIdVal); lastFormArgs.htmlOptions = htmlOptions || {}; lastFormArgs.htmlOptions.id = lastFormArgs.htmlOptions.id || res.defaultFormId(lastFormArgs); lastFormArgs.htmlOptions.method = lastFormArgs.htmlOptions.method || 'post'; var result = [ '
"; }, inputTag : function(name, value, inputType, htmlOptions) { htmlOptions = htmlOptions || {}; res.htmlOptionsPrepareConfirm(htmlOptions); if (value == null) value = ""; value = value.replace(/\"/g, '"'); var result = [''); return result.join(''); }, inputField : function(objName, attrName, inputType, htmlOptions) { htmlOptions = htmlOptions || {}; var value = null; if (res[objName] != null) value = res[objName][attrName]; return res.inputTag(objName + '[' + attrName + ']', value, inputType, htmlOptions); }, textField : function(objName, attrName, htmlOptions) { return res.inputField(objName, attrName, 'text', htmlOptions); }, hiddenField : function(objName, attrName, htmlOptions) { return res.inputField(objName, attrName, 'hidden', htmlOptions); }, passwordField : function(objName, attrName, htmlOptions) { return res.inputField(objName, attrName, 'password', htmlOptions); }, radioButton : function(objName, attrName, tagValue, htmlOptions) { htmlOptions = htmlOptions || {}; if (res[objName] != null && res[objName][attrName] == tagValue) htmlOptions.checked = 'checked'; return res.inputTag(objName + '[' + attrName + ']', tagValue, 'radio', htmlOptions); }, select : function(objName, attrName, choices, htmlOptions) { htmlOptions = htmlOptions || {}; var value = null; if (res[objName] != null) value = res[objName][attrName]; var result = [''); return result.join(''); }, optionsForSelect : function(choices, selected) { var result = []; for (var i = 0; i < choices.length; i++) { var name, value; var choice = choices[i]; if (choice != null && choice instanceof Array) { name = choice[0]; value = choice[1]; } else name = value = choice; if (value == null) value = ''; result.push(''); } return result.join(''); }, textArea : function(objName, attrName, htmlOptions) { var value = null; if (res[objName] != null) value = res[objName][attrName]; if (value == null) value = ""; var result = [''); return result.join(''); }, submitButton : function(name, value, htmlOptions) { return res.inputTag(name, value, 'submit', htmlOptions); }, submitButtonLocal : function(name, value, htmlOptions) { htmlOptions = htmlOptions || {}; htmlOptions.onclick = [ "TrimPath.junction.env.localPostForm('", lastFormArgs.htmlOptions.id, "', '", lastFormArgs.controllerNameVal, "', '", lastFormArgs.actionNameVal, "'" ]; if (lastFormArgs.objIdVal) htmlOptions.onclick.push(", '" + lastFormArgs.objIdVal + "'"); else htmlOptions.onclick.push(", null"); htmlOptions.onclick.push(", null, '" + name + "'"); htmlOptions.onclick.push('); return false'); htmlOptions.onclick = htmlOptions.onclick.join(''); return res.submitButton(name, value, htmlOptions) }, errorMessagesOn : function(objName, attrName, prependText, appendText, cssClass) { if (res[objName] != null && res[objName].errors().isInvalid(attrName)) { prependText = prependText || ""; appendText = appendText || ""; cssClass = cssClass || "formError"; var start = '