+function assert(outcome, description) {
+ if (!outcome) {
+ console.log(description);
+ }
+}
+
+function templateFromId(id) {
+ return _.template($(id).html());
+}
+
+function firstCharUpper(s) {
+ return s.charAt(0).toUpperCase() + s.slice(1);
+}
+
+HTMLView = Marionette.ItemView.extend({
+ render: function() {
+ this.$el.append(this.options.html);
+ },
+});
+
XOSApplication = Marionette.Application.extend({
detailBoxId: "#detailBox",
errorBoxId: "#errorBox",
successCloseButtonId: "#close-success-box",
errorTemplate: "#xos-error-template",
successTemplate: "#xos-success-template",
+ logMessageCount: 0,
- hideError: function(result) {
- $(this.errorBoxId).hide();
- $(this.successBoxId).hide();
+ confirmDialog: function(view, event) {
+ $("#xos-confirm-dialog").dialog({
+ autoOpen: false,\r
+ modal: true,\r
+ buttons : {\r
+ "Confirm" : function() {\r
+ $(this).dialog("close");\r
+ view.trigger(event);\r
+ },\r
+ "Cancel" : function() {\r
+ $(this).dialog("close");\r
+ }\r
+ }\r
+ });
+ $("#xos-confirm-dialog").dialog("open");
+ },
+
+ hideError: function() {
+ if (this.logWindowId) {
+ } else {
+ $(this.errorBoxId).hide();
+ $(this.successBoxId).hide();
+ }
},
showSuccess: function(result) {
- $(this.successBoxId).show();
- $(this.successBoxId).html(_.template($(this.successTemplate).html())(result));
- $(this.successCloseButtonId).unbind().bind('click', function() {
- $(this.successBoxId).hide();
- });
+ 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) {
- $(this.errorBoxId).show();
- $(this.errorBoxId).html(_.template($(this.errorTemplate).html())(result));
- $(this.errorCloseButtonId).unbind().bind('click', function() {
- $(this.errorBoxId).hide();
- });
+ result["statusclass"] = "failure";
+ if (this.logTableId) {
+ this.appendLogWindow(result);
+ } else {
+ $(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) );
+ }
+
+ return logMessageId;
},
+
+ hideLinkedItems: function(result) {
+ var index=0;
+ while (index<4) {\r
+ this["linkedObjs" + (index+1)].empty();\r
+ index = index + 1;\r
+ }\r
+ },\r
+\r
+ listViewShower: function(listViewName, collection_name, regionName, title) {\r
+ var app=this;\r
+ return function() {\r
+ app[regionName].show(new app[listViewName]);\r
+ app.hideLinkedItems();\r
+ $("#contentTitle").html(templateFromId("#xos-title-list")({"title": title}));\r
+ $("#detail").show();\r
+ $("#xos-listview-button-box").show();\r
+ $("#tabs").hide();\r
+ $("#xos-detail-button-box").hide();\r
+ }\r
+ },\r
+\r
+ addShower: function(detailName, collection_name, regionName, title) {\r
+ var app=this;\r
+ return function() {\r
+ model = new xos[collection_name].model();\r
+ detailViewClass = app[detailName];\r
+ detailView = new detailViewClass({model: model, collection:xos[collection_name]});\r
+ app[regionName].show(detailView);\r
+ $("#xos-detail-button-box").show();\r
+ $("#xos-listview-button-box").hide();\r
+ }\r
+ },\r
+\r
+ detailShower: function(detailName, collection_name, regionName, title) {\r
+ var app=this;\r
+ showModelId = function(model_id) {\r
+ $("#contentTitle").html(templateFromId("#xos-title-detail")({"title": title}));\r
+\r
+ collection = xos[collection_name];\r
+ model = collection.get(model_id);\r
+ if (model == undefined) {\r
+ app[regionName].show(new HTMLView({html: "failed to load object " + model_id + " from collection " + collection_name}));\r
+ } else {\r
+ detailViewClass = app[detailName];\r
+ detailView = new detailViewClass({model: model});\r
+ app[regionName].show(detailView);\r
+ detailView.showLinkedItems();\r
+ $("#xos-detail-button-box").show();\r
+ $("#xos-listview-button-box").hide();\r
+ }\r
+ }\r
+ return showModelId;\r
+ },\r
});
/* XOSDetailView
XOSDetailView = Marionette.ItemView.extend({
tagName: "div",
- events: {"click button.js-submit": "submitClicked",
+ 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"},
- events: {"click button.js-submit": "submitClicked",
- "change input": "inputChanged"},
+ initialize: function() {
+ this.on('deleteConfirmed', this.deleteConfirmed);
+ },
/* 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\r
this.dirty = true;\r
},\r
\r
- saveError: function(model, result, xhr) {\r
+ saveError: function(model, result, xhr, infoMsgId) {\r
+ result["what"] = "save " + model.__proto__.modelName;\r
+ result["infoMsgId"] = infoMsgId;\r
+ this.app.showError(result);\r
+ },\r
+\r
+ saveSuccess: function(model, result, xhr, infoMsgId) {\r
+ result = {status: xhr.xhr.status, statusText: xhr.xhr.statusText};\r
+ result["what"] = "save " + model.__proto__.modelName;\r
+ result["infoMsgId"] = infoMsgId;\r
+ this.app.showSuccess(result);\r
+ },
+
+ destroyError: function(model, result, xhr, infoMsgId) {
+ result["what"] = "destroy " + model.__proto__.modelName;\r
+ result["infoMsgId"] = infoMsgId;\r
this.app.showError(result);\r
},\r
\r
- saveSuccess: function(model, result, xhr) {\r
- this.app.showSuccess({status: xhr.xhr.status, statusText: xhr.xhr.statusText});\r
+ destroySuccess: function(model, result, xhr, infoMsgId) {\r
+ result = {status: xhr.xhr.status, statusText: xhr.xhr.statusText};\r
+ result["what"] = "destroy " + model.__proto__.modelName;\r
+ result["infoMsgId"] = infoMsgId;\r
+ this.app.showSuccess(result);\r
},
- submitClicked: function(e) {
- this.app.hideError();\r
- e.preventDefault();\r
+ submitContinueClicked: function(e) {
+ console.log("saveContinue");
+ e.preventDefault();
+ this.save();
+ },
+
+ submitLeaveClicked: function(e) {
+ console.log("saveLeave");
+ e.preventDefault();
+ this.save();
+ this.app.navigate("list", this.model.modelName);
+ },
+
+ submitAddAnotherClicked: function(e) {
+ console.log("saveAnother");
+ e.preventDefault();
+ this.save();
+ this.app.navigate("add", this.model.modelName);
+ },
+
+ save: function() {
+ this.app.hideError();
+ var infoMsgId = this.app.showInformational( {what: "save " + this.model.__proto__.modelName, status: "", statusText: "in progress..."} );\r
var data = Backbone.Syphon.serialize(this);\r
- var thisView = this;\r
- this.model.save(data, {error: function(model, result, xhr) { thisView.saveError(model, result, xhr); },\r
- success: function(model, result, xhr) { thisView.saveSuccess(model, result, xhr); }});\r
+ var that = this;\r
+ var isNew = !this.model.id;\r
+ this.model.save(data, {error: function(model, result, xhr) { that.saveError(model,result,xhr,infoMsgId);},\r
+ success: function(model, result, xhr) { that.saveSuccess(model,result,xhr,infoMsgId);}});\r
+ if (isNew) {\r
+ console.log(this.model);\r
+ this.collection.add(this.model);\r
+ this.collection.sort();\r
+ }\r
this.dirty = false;\r
},
+ destroyModel: function() {
+ this.app.hideError();
+ var infoMsgId = this.app.showInformational( {what: "destroy " + this.model.__proto__.modelName, status: "", statusText: "in progress..."} );
+ var that = this;
+ 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);}});
+ },
+
+ deleteClicked: function(e) {
+ e.preventDefault();
+\r this.app.confirmDialog(this, "deleteConfirmed");
+\r },
+\r
+\r deleteConfirmed: function() {
+\r modelName = this.model.modelName;
+\r this.destroyModel();
+\r this.app.navigate("list", modelName);
+\r },
+\r
+ tabClick: function(tabId, regionName) {
+ region = this.app[regionName];\r
+ if (this.currentTabRegion != undefined) {\r
+ this.currentTabRegion.$el.hide();\r
+ }\r
+ if (this.currentTabId != undefined) {\r
+ $(this.currentTabId).removeClass('active');\r
+ }\r
+ this.currentTabRegion = region;\r
+ this.currentTabRegion.$el.show();\r
+\r
+ this.currentTabId = tabId;\r
+ $(tabId).addClass('active');\r
+ },
+
+ 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() {
- index=0;\r
+ tabs=[];
+
+ tabs.push({name: "details", region: "detail"});
+
+ var index=0;
for (relatedName in this.model.collection.relatedCollections) {\r
relatedField = this.model.collection.relatedCollections[relatedName];\r
+ regionName = "linkedObjs" + (index+1);\r
\r
relatedListViewClassName = relatedName + "ListView";\r
- if (this.app[relatedListViewClassName] == undefined) {\r
- console.log("warning: " + relatedListViewClassName + " not found");\r
- }\r
+ assert(this.app[relatedListViewClassName] != undefined, relatedListViewClassName + " not found");\r
relatedListViewClass = this.app[relatedListViewClassName].extend({collection: xos[relatedName].filterBy(relatedField,this.model.id)});\r
- this.app["linkedObjs" + (index+1)].show(new relatedListViewClass());\r
+ this.app[regionName].show(new relatedListViewClass());\r
+ if (this.app.hideTabsByDefault) {\r
+ this.app[regionName].$el.hide();\r
+ }\r
+ tabs.push({name: relatedName, region: regionName});\r
index = index + 1;\r
}\r
\r
this.app["linkedObjs" + (index+1)].empty();\r
index = index + 1;\r
}\r
- },
-});
+\r
+ this.showTabs(tabs);\r
+ this.tabClick('#xos-nav-detail', 'detail');\r
+ },\r
+\r
+});\r
/* XOSItemView
This is for items that will be displayed as table rows.
XOSListView = Marionette.CompositeView.extend({
childViewContainer: 'tbody',\r
\r
- initialize: function() {\r
- this.listenTo(this.collection, 'change', this._renderChildren)
+ events: {"click button.btn-xos-add": "addClicked",\r
+ "click button.btn-xos-refresh": "refreshClicked",\r
+ },\r
+\r
+ _fetchStateChange: function() {\r
+ if (this.collection.fetching) {\r
+ $("#xos-list-title-spinner").show();\r
+ } else {\r
+ $("#xos-list-title-spinner").hide();\r
+ }\r
+ },\r
+\r
+ addClicked: function(e) {
+ e.preventDefault();
+ this.app.Router.navigate("add" + firstCharUpper(this.collection.modelName), {trigger: true});
+ },
+\r
+\r refreshClicked: function(e) {
+\r e.preventDefault();
+\r this.collection.refresh(refreshRelated=true);
+\r },
+\r
+\r initialize: function() {
+\r this.listenTo(this.collection, 'change', this._renderChildren)
+ 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