blob: 2a1e94f89955f4dd9c1d74a0f09f1df7c27502e6 [file] [log] [blame]
HTMLView = Marionette.ItemView.extend({
render: function() {
this.$el.append(this.options.html);
},
});
FilteredCompositeView = Marionette.CompositeView.extend( {
showCollection: function() {
var ChildView;
this.collection.each(function(child, index) {
if (this.filter && !this.filter(child)) {
return;
}
ChildView = this.getChildView(child);
this.addChild(child, ChildView, index);
}, this);
},
});
XOSRouter = Marionette.AppRouter.extend({
initialize: function() {
this.routeStack=[];
},
onRoute: function(x,y,z) {
this.routeStack.push(Backbone.history.fragment);
},
prevPage: function() {
return this.routeStack.slice(-1)[0];
},
showPreviousURL: function() {
prevPage = this.prevPage();
console.log("showPreviousURL");
console.log(this.routeStack);
if (prevPage) {
this.navigate("#"+prevPage, {trigger: false, replace: true} );
}
},
navigate: function(href, options) {
if (options.force) {
Marionette.AppRouter.prototype.navigate.call(this, "nowhere", {trigger: false, replace: true});
}
Marionette.AppRouter.prototype.navigate.call(this, href, options);
},
});
XOSApplication = Marionette.Application.extend({
detailBoxId: "#detailBox",
errorBoxId: "#errorBox",
errorCloseButtonId: "#close-error-box",
successBoxId: "#successBox",
successCloseButtonId: "#close-success-box",
errorTemplate: "#xos-error-template",
successTemplate: "#xos-success-template",
logMessageCount: 0,
confirmDialog: function(view, event, callback) {
$("#xos-confirm-dialog").dialog({
autoOpen: false,
modal: true,
buttons : {
"Confirm" : function() {
$(this).dialog("close");
if (event) {
view.trigger(event);
}
if (callback) {
callback();
}
},
"Cancel" : function() {
$(this).dialog("close");
}
}
});
$("#xos-confirm-dialog").dialog("open");
},
popupErrorDialog: function(responseText) {
try {
parsed_error=$.parseJSON(responseText);
width=300;
}
catch(err) {
parsed_error=undefined;
width=640; // django stacktraces like wide width
}
if (parsed_error) {
$("#xos-error-dialog").html(templateFromId("#xos-error-response")(parsed_error));
} else {
$("#xos-error-dialog").html(templateFromId("#xos-error-rawresponse")({responseText: responseText}))
}
$("#xos-error-dialog").dialog({
modal: true,
width: width,
buttons: {
Ok: function() { $(this).dialog("close"); }
}
});
},
hideLinkedItems: function(result) {
var index=0;
while (index<4) {
this["linkedObjs" + (index+1)].empty();
index = index + 1;
}
},
hideTabs: function() { $("#tabs").hide(); },
showTabs: function() { $("#tabs").show(); },
createListHandler: function(listViewName, collection_name, regionName, title) {
var app=this;
return function() {
listView = new app[listViewName];
app[regionName].show(listView);
app.hideLinkedItems();
$("#contentTitle").html(templateFromId("#xos-title-list")({"title": title}));
$("#detail").show();
app.hideTabs();
listButtons = new XOSListButtonView({linkedView: listView});
app["rightButtonPanel"].show(listButtons);
}
},
createAddHandler: function(detailName, collection_name, regionName, title) {
var app=this;
return function() {
console.log("addHandler");
app.hideLinkedItems();
app.hideTabs();
model = new xos[collection_name].model();
detailViewClass = app[detailName];
detailView = new detailViewClass({model: model, collection:xos[collection_name]});
app[regionName].show(detailView);
detailButtons = new XOSDetailButtonView({linkedView: detailView});
app["rightButtonPanel"].show(detailButtons);
}
},
createAddChildHandler: function(addChildName, collection_name) {
var app=this;
return function(parent_modelName, parent_fieldName, parent_id) {
app.Router.showPreviousURL();
model = new xos[collection_name].model();
model.attributes[parent_fieldName] = parent_id;
model.readOnlyFields.push(parent_fieldName);
detailViewClass = app[addChildName];
var detailView = new detailViewClass({model: model, collection:xos[collection_name]});
detailView.dialog = $("xos-addchild-dialog");
app["addChildDetail"].show(detailView);
$("#xos-addchild-dialog").dialog({
autoOpen: false,
modal: true,
width: 640,
buttons : {
"Save" : function() {
var addDialog = this;
detailView.synchronous = true;
detailView.afterSave = function() { console.log("addChild afterSave"); $(addDialog).dialog("close"); }
detailView.save();
//$(this).dialog("close");
},
"Cancel" : function() {
$(this).dialog("close");
}
}
});
$("#xos-addchild-dialog").dialog("open");
}
},
createDeleteHandler: function(collection_name) {
var app=this;
return function(model_id) {
console.log("deleteCalled");
collection = xos[collection_name];
model = collection.get(model_id);
assert(model!=undefined, "failed to get model " + model_id + " from collection " + collection_name);
app.Router.showPreviousURL();
app.deleteDialog(model);
}
},
createDetailHandler: function(detailName, collection_name, regionName, title) {
var app=this;
showModelId = function(model_id) {
$("#contentTitle").html(templateFromId("#xos-title-detail")({"title": title}));
collection = xos[collection_name];
model = collection.get(model_id);
if (model == undefined) {
app[regionName].show(new HTMLView({html: "failed to load object " + model_id + " from collection " + collection_name}));
} else {
detailViewClass = app[detailName];
detailView = new detailViewClass({model: model});
app[regionName].show(detailView);
detailView.showLinkedItems();
detailButtons = new XOSDetailButtonView({linkedView: detailView});
app["rightButtonPanel"].show(detailButtons);
}
}
return showModelId;
},
/* error handling callbacks */
hideError: function() {
if (this.logWindowId) {
} else {
$(this.errorBoxId).hide();
$(this.successBoxId).hide();
}
},
showSuccess: function(result) {
result["statusclass"] = "success";
if (this.logTableId) {
this.appendLogWindow(result);
} else {
$(this.successBoxId).show();
$(this.successBoxId).html(_.template($(this.successTemplate).html())(result));
var that=this;
$(this.successCloseButtonId).unbind().bind('click', function() {
$(that.successBoxId).hide();
});
}
},
showError: function(result) {
result["statusclass"] = "failure";
if (this.logTableId) {
this.appendLogWindow(result);
this.popupErrorDialog(result.responseText);
} else {
// this is really old stuff
$(this.errorBoxId).show();
$(this.errorBoxId).html(_.template($(this.errorTemplate).html())(result));
var that=this;
$(this.errorCloseButtonId).unbind().bind('click', function() {
$(that.errorBoxId).hide();
});
}
},
showInformational: function(result) {
result["statusclass"] = "inprog";
if (this.logTableId) {
return this.appendLogWindow(result);
} else {
return undefined;
}
},
appendLogWindow: function(result) {
// compute a new logMessageId for this log message
logMessageId = "logMessage" + this.logMessageCount;
this.logMessageCount = this.logMessageCount + 1;
result["logMessageId"] = logMessageId;
logMessageTemplate=$("#xos-log-template").html();
assert(logMessageTemplate != undefined, "logMessageTemplate is undefined");
newRow = _.template(logMessageTemplate, result);
assert(newRow != undefined, "newRow is undefined");
if (result["infoMsgId"] != undefined) {
// We were passed the logMessageId of an informational message,
// and the caller wants us to replace that message with our own.
// i.e. replace an informational message with a success or an error.
$("#"+result["infoMsgId"]).replaceWith(newRow);
} else {
// Create a brand new log message rather than replacing one.
logTableBody = $(this.logTableId + " tbody");
logTableBody.prepend(newRow);
}
if (this.statusMsgId) {
$(this.statusMsgId).html( templateFromId("#xos-status-template")(result) );
}
limitTableRows(this.logTableId, 5);
return logMessageId;
},
saveError: function(model, result, xhr, infoMsgId) {
console.log("saveError");
result["what"] = "save " + model.modelName + " " + model.attributes.humanReadableName;
result["infoMsgId"] = infoMsgId;
this.showError(result);
},
saveSuccess: function(model, result, xhr, infoMsgId, addToCollection) {
console.log("saveSuccess");
if (model.addToCollection) {
console.log("addToCollection");
console.log(model.addToCollection);
model.addToCollection.add(model);
model.addToCollection.sort();
model.addToCollection = undefined;
}
result = {status: xhr.xhr.status, statusText: xhr.xhr.statusText};
result["what"] = "save " + model.modelName + " " + model.attributes.humanReadableName;
result["infoMsgId"] = infoMsgId;
this.showSuccess(result);
},
destroyError: function(model, result, xhr, infoMsgId) {
result["what"] = "destroy " + model.modelName + " " + model.attributes.humanReadableName;
result["infoMsgId"] = infoMsgId;
this.showError(result);
},
destroySuccess: function(model, result, xhr, infoMsgId) {
result = {status: xhr.xhr.status, statusText: xhr.xhr.statusText};
result["what"] = "destroy " + model.modelName + " " + model.attributes.humanReadableName;
result["infoMsgId"] = infoMsgId;
this.showSuccess(result);
},
/* end error handling callbacks */
destroyModel: function(model) {
//console.log("destroyModel"); console.log(model);
this.hideError();
var infoMsgId = this.showInformational( {what: "destroy " + model.modelName + " " + model.attributes.humanReadableName, status: "", statusText: "in progress..."} );
var that = this;
model.destroy({error: function(model, result, xhr) { that.destroyError(model,result,xhr,infoMsgId);},
success: function(model, result, xhr) { that.destroySuccess(model,result,xhr,infoMsgId);}});
},
deleteDialog: function(model, afterDelete) {
var that=this;
assert(model!=undefined, "deleteDialog's model is undefined");
//console.log("deleteDialog"); console.log(model);
this.confirmDialog(null, null, function() {
//console.log("deleteConfirm"); console.log(model);
modelName = model.modelName;
that.destroyModel(model);
if (afterDelete=="list") {
that.navigate("list", modelName);
}
});
},
});
XOSButtonView = Marionette.ItemView.extend({
events: {"click button.btn-xos-save-continue": "submitContinueClicked",
"click button.btn-xos-save-leave": "submitLeaveClicked",
"click button.btn-xos-save-another": "submitAddAnotherClicked",
"click button.btn-xos-delete": "deleteClicked",
"click button.btn-xos-add": "addClicked",
"click button.btn-xos-refresh": "refreshClicked",
},
submitLeaveClicked: function(e) {
this.options.linkedView.submitLeaveClicked.call(this.options.linkedView, e);
},
submitContinueClicked: function(e) {
this.options.linkedView.submitContinueClicked.call(this.options.linkedView, e);
},
submitAddAnotherClicked: function(e) {
this.options.linkedView.submitAddAnotherClicked.call(this.options.linkedView, e);
},
submitDeleteClicked: function(e) {
this.options.linkedView.submitDeleteClicked.call(this.options.linkedView, e);
},
addClicked: function(e) {
this.options.linkedView.addClicked.call(this.options.linkedView, e);
},
refreshClicked: function(e) {
this.options.linkedView.refreshClicked.call(this.options.linkedView, e);
},
});
XOSDetailButtonView = XOSButtonView.extend({ template: "#xos-savebuttons-template" });
XOSListButtonView = XOSButtonView.extend({ template: "#xos-listbuttons-template" });
/* XOSDetailView
extend with:
app - MarionetteApplication
template - template (See XOSHelper.html)
*/
XOSDetailView = Marionette.ItemView.extend({
tagName: "div",
events: {"click button.btn-xos-save-continue": "submitContinueClicked",
"click button.btn-xos-save-leave": "submitLeaveClicked",
"click button.btn-xos-save-another": "submitAddAnotherClicked",
"click button.btn-xos-delete": "deleteClicked",
"change input": "inputChanged"},
/* inputChanged is watching the onChange events of the input controls. We
do this to track when this view is 'dirty', so we can throw up a warning
if the user tries to change his slices without saving first.
*/
initialize: function() {
this.on("saveSuccess", this.onAfterSave);
this.synchronous = false;
},
afterSave: function(e) {
},
onAfterSave: function(e) {
this.afterSave(e);
},
inputChanged: function(e) {
this.dirty = true;
},
submitContinueClicked: function(e) {
console.log("saveContinue");
e.preventDefault();
this.afterSave = function() { };
this.save();
},
submitLeaveClicked: function(e) {
console.log("saveLeave");
e.preventDefault();
var that=this;
this.afterSave = function() {
that.app.navigate("list", that.model.modelName);
}
this.save();
},
submitAddAnotherClicked: function(e) {
console.log("saveAnother");
console.log(this);
e.preventDefault();
var that=this;
this.afterSave = function() {
console.log("addAnother afterSave");
that.app.navigate("add", that.model.modelName);
}
this.save();
},
save: function() {
this.app.hideError();
var data = Backbone.Syphon.serialize(this);
var that = this;
var isNew = !this.model.id;
this.$el.find(".help-inline").remove();
/* although model.validate() is called automatically by
model.save, we call it ourselves, so we can throw up our
validation error before creating the infoMsg in the log
*/
errors = this.model.xosValidate(data);
if (errors) {
this.onFormDataInvalid(errors);
return;
}
if (isNew) {
this.model.attributes.humanReadableName = "new " + model.modelName;
this.model.addToCollection = this.collection;
} else {
this.model.addToCollection = undefined;
}
var infoMsgId = this.app.showInformational( {what: "save " + model.modelName + " " + model.attributes.humanReadableName, status: "", statusText: "in progress..."} );
this.model.save(data, {error: function(model, result, xhr) { that.app.saveError(model,result,xhr,infoMsgId);},
success: function(model, result, xhr) { that.app.saveSuccess(model,result,xhr,infoMsgId);
if (that.synchronous) {
that.trigger("saveSuccess");
}
}});
this.dirty = false;
if (!this.synchronous) {
this.afterSave();
}
},
deleteClicked: function(e) {
e.preventDefault();
this.app.deleteDialog(this.model, "list");
},
tabClick: function(tabId, regionName) {
region = this.app[regionName];
if (this.currentTabRegion != undefined) {
this.currentTabRegion.$el.hide();
}
if (this.currentTabId != undefined) {
$(this.currentTabId).removeClass('active');
}
this.currentTabRegion = region;
this.currentTabRegion.$el.show();
this.currentTabId = tabId;
$(tabId).addClass('active');
},
showTabs: function(tabs) {
template = templateFromId("#xos-tabs-template", {tabs: tabs});
$("#tabs").html(template(tabs));
var that = this;
_.each(tabs, function(tab) {
var regionName = tab["region"];
var tabId = '#xos-nav-'+regionName;
$(tabId).bind('click', function() { that.tabClick(tabId, regionName); });
});
$("#tabs").show();
},
showLinkedItems: function() {
tabs=[];
tabs.push({name: "details", region: "detail"});
makeFilter = function(relatedField, relatedId) {
return function(model) { return model.attributes[relatedField] == relatedId; }
};
var index=0;
for (relatedName in this.model.collection.relatedCollections) {
var relatedField = this.model.collection.relatedCollections[relatedName];
var relatedId = this.model.id;
regionName = "linkedObjs" + (index+1);
relatedListViewClassName = relatedName + "ListView";
assert(this.app[relatedListViewClassName] != undefined, relatedListViewClassName + " not found");
relatedListViewClass = this.app[relatedListViewClassName].extend({collection: xos[relatedName],
filter: makeFilter(relatedField, relatedId),
parentModel: this.model});
this.app[regionName].show(new relatedListViewClass());
if (this.app.hideTabsByDefault) {
this.app[regionName].$el.hide();
}
tabs.push({name: relatedName, region: regionName});
index = index + 1;
}
while (index<4) {
this.app["linkedObjs" + (index+1)].empty();
index = index + 1;
}
this.showTabs(tabs);
this.tabClick('#xos-nav-detail', 'detail');
},
onFormDataInvalid: function(errors) {
var self=this;
var markErrors = function(value, key) {
console.log("name='" + key + "'");
var $inputElement = self.$el.find("[name='" + key + "']");
var $inputContainer = $inputElement.parent();
//$inputContainer.find(".help-inline").remove();
var $errorEl = $("<span>", {class: "help-inline error", text: value});
$inputContainer.append($errorEl).addClass("error");
}
_.each(errors, markErrors);
},
templateHelpers: function() { return { modelName: this.model.modelName,
collectionName: this.model.collectionName,
addFields: this.model.addFields,
listFields: this.model.listFields,
detailFields: this.model.detailFields,
foreignFields: this.model.foreignFields,
detailLinkFields: this.model.detailLinkFields,
inputType: this.model.inputType,
model: this.model,
}},
});
/* XOSItemView
This is for items that will be displayed as table rows.
extend with:
app - MarionetteApplication
template - template (See XOSHelper.html)
*/
XOSItemView = Marionette.ItemView.extend({
tagName: 'tr',
className: 'test-tablerow',
templateHelpers: function() { return { modelName: this.model.modelName,
collectionName: this.model.collectionName,
listFields: this.model.listFields,
addFields: this.model.addFields,
detailFields: this.model.detailFields,
foreignFields: this.model.foreignFields,
detailLinkFields: this.model.detailLinkFields,
inputType: this.model.inputType,
model: this.model,
}},
});
/* XOSListView:
extend with:
app - MarionetteApplication
childView - class of ItemView, probably an XOSItemView
template - template (see xosHelper.html)
collection - collection that holds these objects
title - title to display in template
*/
XOSListView = FilteredCompositeView.extend({
childViewContainer: 'tbody',
parentModel: null,
events: {"click button.btn-xos-add": "addClicked",
"click button.btn-xos-refresh": "refreshClicked",
},
_fetchStateChange: function() {
if (this.collection.fetching) {
$("#xos-list-title-spinner").show();
} else {
$("#xos-list-title-spinner").hide();
}
},
addClicked: function(e) {
e.preventDefault();
this.app.Router.navigate("add" + firstCharUpper(this.collection.modelName), {trigger: true});
},
refreshClicked: function(e) {
e.preventDefault();
this.collection.refresh(refreshRelated=true);
},
initialize: function() {
this.listenTo(this.collection, 'change', this._renderChildren)
this.listenTo(this.collection, 'sort', function() { console.log("sort"); })
this.listenTo(this.collection, 'add', function() { console.log("add"); })
this.listenTo(this.collection, 'fetchStateChange', this._fetchStateChange);
// Because many of the templates use idToName(), we need to
// listen to the collections that hold the names for the ids
// that we want to display.
for (i in this.collection.foreignCollections) {
foreignName = this.collection.foreignCollections[i];
if (xos[foreignName] == undefined) {
console.log("Failed to find xos class " + foreignName);
}
this.listenTo(xos[foreignName], 'change', this._renderChildren);
this.listenTo(xos[foreignName], 'sort', this._renderChildren);
}
},
getAddChildHash: function() {
if (this.parentModel) {
parentFieldName = this.parentModel.relatedCollections[this.collection.collectionName];
parentFieldName = parentFieldName || "unknown";
/*parentFieldName = "unknown";
for (fieldName in this.collection.foreignFields) {
cname = this.collection.foreignFields[fieldName];
if (cname = this.collection.collectionName) {
parentFieldName = fieldName;
}
}*/
return "#addChild" + firstCharUpper(this.collection.modelName) + "/" + this.parentModel.modelName + "/" + parentFieldName + "/" + this.parentModel.id; // modelName, fieldName, id
} else {
return null;
}
},
templateHelpers: function() {
return { title: this.title,
addChildHash: this.getAddChildHash(),
foreignFields: this.collection.foreignFields,
listFields: this.collection.listFields,
detailLinkFields: this.collection.detailLinkFields, };
},
});
XOSDataTableView = Marionette.View.extend( {
el: '<div style="overflow: hidden">' +
'<h3 class="xos-list-title title_placeholder"></h3>' +
'<div class="header_placeholder"></div>' +
'<table></table>' +
'<div class="footer_placeholder"></div>' +
'</div>',
filter: undefined,
events: {"click button.btn-xos-add": "addClicked",
"click button.btn-xos-refresh": "refreshClicked",
},
_fetchStateChange: function() {
if (this.collection.fetching) {
$("#xos-list-title-spinner").show();
} else {
$("#xos-list-title-spinner").hide();
}
},
addClicked: function(e) {
e.preventDefault();
this.app.Router.navigate("add" + firstCharUpper(this.collection.modelName), {trigger: true});
},
refreshClicked: function(e) {
e.preventDefault();
this.collection.refresh(refreshRelated=true);
},
initialize: function() {
$(this.el).find(".footer_placeholder").html( xosListFooterTemplate({addChildHash: this.getAddChildHash()}) );
$(this.el).find(".header_placeholder").html( xosListHeaderTemplate() );
this.listenTo(this.collection, 'fetchStateChange', this._fetchStateChange);
},
render: function() {
var view = this;
view.columnsByIndex = [];
view.columnsByFieldName = {};
_.each(this.collection.listFields, function(fieldName) {
mRender = undefined;
mSearchText = undefined;
if (fieldName in view.collection.foreignFields) {
var foreignCollection = view.collection.foreignFields[fieldName];
mSearchText = function(x) { return idToName(x, foreignCollection, "humanReadableName"); };
}
if ($.inArray(fieldName, view.collection.detailLinkFields)>=0) {
var collectionName = view.collection.collectionName;
mRender = function(x,y,z) { return '<a href="#' + collectionName + '/' + z.id + '">' + x + '</a>'; };
}
thisColumn = {sTitle: fieldNameToHumanReadable(fieldName), mData: fieldName, mRender: mRender, mSearchText: mSearchText};
view.columnsByIndex.push( thisColumn );
view.columnsByFieldName[fieldName] = thisColumn;
});
deleteColumn = {sTitle: "delete", mRender: function(x,y,z) { return xosDeleteButtonTemplate({modelName: view.collection.modelName, id: z.id}); }, mData: function() { return "delete"; }};
view.columnsByIndex.push(deleteColumn);
view.columnsByFieldName["delete"] = deleteColumn;
oTable = $(this.el).find("table").dataTable( {
"bJQueryUI": true,
"bStateSave": true,
"bServerSide": true,
"aoColumns": view.columnsByIndex,
fnServerData: function(sSource, aoData, fnCallback, settings) {
var compareColumns = function(sortCols, sortDirs, a, b) {
a = a[sortCols[0]];
b = b[sortCols[0]];
result = (a==b) ? 0 : ((a<b) ? -1 : 1);
if (sortDirs[0] == "desc") {
result = -result;
}
return result;
};
var searchMatch = function(row, sSearch) {
for (fieldName in row) {
if (fieldName in view.columnsByFieldName) {
try {
value = row[fieldName].toString();
} catch(e) {
continue;
}
if (value.indexOf(sSearch) >= 0) {
return true;
}
}
}
return false;
};
//console.log(aoData);
// function used to populate the DataTable with the current
// content of the collection
var populateTable = function()
{
console.log("populatetable!");
// clear out old row views
rows = [];
sSearch = null;
iDisplayStart = 0;
iDisplayLength = 1000;
sortDirs = [];
sortCols = [];
_.each(aoData, function(param) {
if (param.name == "sSortDir_0") {
sortDirs = [param.value];
} else if (param.name == "iSortCol_0") {
sortCols = [view.columnsByIndex[param.value].mData];
} else if (param.name == "iDisplayStart") {
iDisplayStart = param.value;
} else if (param.name == "iDisplayLength") {
iDisplayLength = param.value;
} else if (param.name == "sSearch") {
sSearch = param.value;
}
});
aaData = view.collection.toJSON();
// apply backbone filtering on the models
if (view.filter) {
aaData = aaData.filter( function(row) { model = {}; model.attributes = row; return view.filter(model); } );
}
var totalSize = aaData.length;
// turn the ForeignKey fields into human readable things
for (rowIndex in aaData) {
row = aaData[rowIndex];
for (fieldName in row) {
if (fieldName in view.columnsByFieldName) {
mSearchText = view.columnsByFieldName[fieldName].mSearchText;
if (mSearchText) {
row[fieldName] = mSearchText(row[fieldName]);
}
}
}
}
// apply datatables search
if (sSearch) {
aaData = aaData.filter( function(row) { return searchMatch(row, sSearch); });
}
var filteredSize = aaData.length;
// apply datatables sort
aaData.sort(function(a,b) { return compareColumns(sortCols, sortDirs, a, b); });
// slice it for pagination
aaData = aaData.slice(iDisplayStart, iDisplayStart+iDisplayLength);
return fnCallback({iTotalRecords: totalSize,
iTotalDisplayRecords: filteredSize,
aaData: aaData});
};
aoData.shift(); // ignore sEcho
populateTable();
view.listenTo(view.collection, 'change', populateTable);
view.listenTo(view.collection, 'add', populateTable);
view.listenTo(view.collection, 'remove', populateTable);
},
} );
return this;
},
getAddChildHash: function() {
if (this.parentModel) {
parentFieldName = this.parentModel.relatedCollections[this.collection.collectionName];
parentFieldName = parentFieldName || "unknown";
/*parentFieldName = "unknown";
for (fieldName in this.collection.foreignFields) {
cname = this.collection.foreignFields[fieldName];
if (cname = this.collection.collectionName) {
parentFieldName = fieldName;
}
}*/
return "#addChild" + firstCharUpper(this.collection.modelName) + "/" + this.parentModel.modelName + "/" + parentFieldName + "/" + this.parentModel.id; // modelName, fieldName, id
} else {
return null;
}
},
});
idToName = function(id, collectionName, fieldName) {
return xos.idToName(id, collectionName, fieldName);
};
/* Constructs lists of <option> html blocks for items in a collection.
selectedId = the id of an object that should be selected, if any
collectionName = name of collection
fieldName = name of field within models of collection that will be displayed
*/
idToOptions = function(selectedId, collectionName, fieldName) {
result=""
for (index in xos[collectionName].models) {
linkedObject = xos[collectionName].models[index];
linkedId = linkedObject["id"];
linkedName = linkedObject.attributes[fieldName];
if (linkedId == selectedId) {
selected = " selected";
} else {
selected = "";
}
result = result + '<option value="' + linkedId + '"' + selected + '>' + linkedName + '</option>';
}
return result;
};
/* Constructs an html <select> and the <option>s to go with it.
variable = variable name to return to form
selectedId = the id of an object that should be selected, if any
collectionName = name of collection
fieldName = name of field within models of collection that will be displayed
*/
idToSelect = function(variable, selectedId, collectionName, fieldName, readOnly) {
if (readOnly) {
readOnly = " readonly";
} else {
readOnly = "";
}
result = '<select name="' + variable + '"' + readOnly + '>' +
idToOptions(selectedId, collectionName, fieldName) +
'</select>';
return result;
}