1 function assert(outcome, description) {
3 console.log(description);
7 function templateFromId(id) {
8 return _.template($(id).html());
11 function firstCharUpper(s) {
12 return s.charAt(0).toUpperCase() + s.slice(1);
15 HTMLView = Marionette.ItemView.extend({
17 this.$el.append(this.options.html);
21 XOSApplication = Marionette.Application.extend({
22 detailBoxId: "#detailBox",
23 errorBoxId: "#errorBox",
24 errorCloseButtonId: "#close-error-box",
25 successBoxId: "#successBox",
26 successCloseButtonId: "#close-success-box",
27 errorTemplate: "#xos-error-template",
28 successTemplate: "#xos-success-template",
31 hideError: function() {
32 if (this.logWindowId) {
34 $(this.errorBoxId).hide();
35 $(this.successBoxId).hide();
39 showSuccess: function(result) {
40 result["statusclass"] = "success";
41 if (this.logTableId) {
42 this.appendLogWindow(result);
44 $(this.successBoxId).show();
45 $(this.successBoxId).html(_.template($(this.successTemplate).html())(result));
47 $(this.successCloseButtonId).unbind().bind('click', function() {
48 $(that.successBoxId).hide();
53 showError: function(result) {
54 result["statusclass"] = "failure";
55 if (this.logTableId) {
56 this.appendLogWindow(result);
58 $(this.errorBoxId).show();
59 $(this.errorBoxId).html(_.template($(this.errorTemplate).html())(result));
61 $(this.errorCloseButtonId).unbind().bind('click', function() {
62 $(that.errorBoxId).hide();
67 showInformational: function(result) {
68 result["statusclass"] = "inprog";
69 if (this.logTableId) {
70 return this.appendLogWindow(result);
76 appendLogWindow: function(result) {
77 // compute a new logMessageId for this log message
78 logMessageId = "logMessage" + this.logMessageCount;
79 this.logMessageCount = this.logMessageCount + 1;
80 result["logMessageId"] = logMessageId;
82 logMessageTemplate=$("#xos-log-template").html();
83 assert(logMessageTemplate != undefined, "logMessageTemplate is undefined");
84 newRow = _.template(logMessageTemplate, result);
85 assert(newRow != undefined, "newRow is undefined");
87 if (result["infoMsgId"] != undefined) {
88 // We were passed the logMessageId of an informational message,
89 // and the caller wants us to replace that message with our own.
90 // i.e. replace an informational message with a success or an error.
91 $("#"+result["infoMsgId"]).replaceWith(newRow);
93 // Create a brand new log message rather than replacing one.
94 logTableBody = $(this.logTableId + " tbody");
95 logTableBody.prepend(newRow);
98 if (this.statusMsgId) {
99 $(this.statusMsgId).html( templateFromId("#xos-status-template")(result) );
105 hideLinkedItems: function(result) {
108 this["linkedObjs" + (index+1)].empty();
\r
113 listViewShower: function(listViewName, collection_name, regionName, title) {
\r
115 return function() {
\r
116 app[regionName].show(new app[listViewName]);
\r
117 app.hideLinkedItems();
\r
118 $("#contentTitle").html(templateFromId("#xos-title-list")({"title": title}));
\r
119 $("#detail").show();
\r
120 $("#xos-listview-button-box").show();
\r
122 $("#xos-detail-button-box").hide();
\r
126 addShower: function(detailName, collection_name, regionName, title) {
\r
128 return function() {
\r
129 model = new xos[collection_name].model();
\r
130 detailViewClass = app[detailName];
\r
131 detailView = new detailViewClass({model: model});
\r
132 app[regionName].show(detailView);
\r
133 $("#xos-detail-button-box").show();
\r
134 $("#xos-listview-button-box").hide();
\r
138 detailShower: function(detailName, collection_name, regionName, title) {
\r
140 showModelId = function(model_id) {
\r
141 $("#contentTitle").html(templateFromId("#xos-title-detail")({"title": title}));
\r
143 collection = xos[collection_name];
\r
144 model = collection.get(model_id);
\r
145 if (model == undefined) {
\r
146 app[regionName].show(new HTMLView({html: "failed to load object " + model_id + " from collection " + collection_name}));
\r
148 detailViewClass = app[detailName];
\r
149 detailView = new detailViewClass({model: model});
\r
150 app[regionName].show(detailView);
\r
151 detailView.showLinkedItems();
\r
152 $("#xos-detail-button-box").show();
\r
153 $("#xos-listview-button-box").hide();
\r
156 return showModelId;
\r
162 app - MarionetteApplication
163 template - template (See XOSHelper.html)
166 XOSDetailView = Marionette.ItemView.extend({
169 events: {"click button.btn-xos-save-continue": "submitContinueClicked",
170 "click button.btn-xos-save-leave": "submitLeaveClicked",
171 "click button.btn-xos-save-another": "submitAddAnotherClicked",
172 "change input": "inputChanged"},
174 /* inputChanged is watching the onChange events of the input controls. We
175 do this to track when this view is 'dirty', so we can throw up a warning
\r
176 if the user tries to change his slices without saving first.
\r
179 inputChanged: function(e) {
\r
183 saveError: function(model, result, xhr, infoMsgId) {
\r
184 result["what"] = "save " + model.__proto__.modelName;
\r
185 result["infoMsgId"] = infoMsgId;
\r
186 this.app.showError(result);
\r
189 saveSuccess: function(model, result, xhr, infoMsgId) {
\r
190 result = {status: xhr.xhr.status, statusText: xhr.xhr.statusText};
\r
191 result["what"] = "save " + model.__proto__.modelName;
\r
192 result["infoMsgId"] = infoMsgId;
\r
193 this.app.showSuccess(result);
\r
196 submitContinueClicked: function(e) {
197 console.log("saveContinue");
202 submitLeaveClicked: function(e) {
203 console.log("saveLeave");
206 this.app.Router.navigate(this.listNavLink, {trigger: true});
207 console.log("route to " + this.listNavLink);
210 submitAddAnotherClicked: function(e) {
211 console.log("saveAnother");
217 this.app.hideError();
218 var infoMsgId = this.app.showInformational( {what: "save " + this.model.__proto__.modelName, status: "", statusText: "in progress..."} );
\r
219 var data = Backbone.Syphon.serialize(this);
\r
221 this.model.save(data, {error: function(model, result, xhr) { that.saveError(model,result,xhr,infoMsgId);},
\r
222 success: function(model, result, xhr) { that.saveSuccess(model,result,xhr,infoMsgId);}});
\r
223 this.dirty = false;
\r
226 tabClick: function(tabId, regionName) {
227 region = this.app[regionName];
\r
228 if (this.currentTabRegion != undefined) {
\r
229 this.currentTabRegion.$el.hide();
\r
231 if (this.currentTabId != undefined) {
\r
232 $(this.currentTabId).removeClass('active');
\r
234 this.currentTabRegion = region;
\r
235 this.currentTabRegion.$el.show();
\r
237 this.currentTabId = tabId;
\r
238 $(tabId).addClass('active');
\r
241 showTabs: function(tabs) {
242 template = templateFromId("#xos-tabs-template", {tabs: tabs});
243 $("#tabs").html(template(tabs));
246 _.each(tabs, function(tab) {
247 var regionName = tab["region"];
248 var tabId = '#xos-nav-'+regionName;
249 $(tabId).bind('click', function() { that.tabClick(tabId, regionName); });
255 showLinkedItems: function() {
258 tabs.push({name: "details", region: "detail"});
261 for (relatedName in this.model.collection.relatedCollections) {
\r
262 relatedField = this.model.collection.relatedCollections[relatedName];
\r
263 regionName = "linkedObjs" + (index+1);
\r
265 relatedListViewClassName = relatedName + "ListView";
\r
266 assert(this.app[relatedListViewClassName] != undefined, relatedListViewClassName + " not found");
\r
267 relatedListViewClass = this.app[relatedListViewClassName].extend({collection: xos[relatedName].filterBy(relatedField,this.model.id)});
\r
268 this.app[regionName].show(new relatedListViewClass());
\r
269 if (this.app.hideTabsByDefault) {
\r
270 this.app[regionName].$el.hide();
\r
272 tabs.push({name: relatedName, region: regionName});
\r
277 this.app["linkedObjs" + (index+1)].empty();
\r
281 this.showTabs(tabs);
\r
282 this.tabClick('#xos-nav-detail', 'detail');
\r
288 This is for items that will be displayed as table rows.
290 app - MarionetteApplication
291 template - template (See XOSHelper.html)
292 detailClass - class of detail view, probably an XOSDetailView
295 XOSItemView = Marionette.ItemView.extend({
297 className: 'test-tablerow',
299 events: {"click": "changeItem"},
301 changeItem: function(e) {
\r
302 this.app.hideError();
\r
303 e.preventDefault();
\r
304 e.stopPropagation();
\r
306 this.app.navigateToModel(this.app, this.detailClass, this.detailNavLink, this.model);
\r
312 app - MarionetteApplication
313 childView - class of ItemView, probably an XOSItemView
314 template - template (see xosHelper.html)
315 collection - collection that holds these objects
316 title - title to display in template
319 XOSListView = Marionette.CompositeView.extend({
320 childViewContainer: 'tbody',
\r
322 events: {"click button.btn-xos-add": "addClicked",
\r
325 addClicked: function(e) {
328 this.app.Router.navigate("add" + firstCharUpper(this.collection.modelName), {trigger: true});
331 initialize: function() {
\r
332 this.listenTo(this.collection, 'change', this._renderChildren)
334 // Because many of the templates use idToName(), we need to
335 // listen to the collections that hold the names for the ids
336 // that we want to display.
337 for (i in this.collection.foreignCollections) {
338 foreignName = this.collection.foreignCollections[i];
339 if (xos[foreignName] == undefined) {
340 console.log("Failed to find xos class " + foreignName);
342 this.listenTo(xos[foreignName], 'change', this._renderChildren);
343 this.listenTo(xos[foreignName], 'sort', this._renderChildren);
347 templateHelpers: function() {
348 return { title: this.title };
352 /* Give an id, the name of a collection, and the name of a field for models
353 within that collection, lookup the id and return the value of the field.
356 idToName = function(id, collectionName, fieldName) {
357 linkedObject = xos[collectionName].get(id);
358 if (linkedObject == undefined) {
361 return linkedObject.attributes[fieldName];
365 /* Constructs lists of <option> html blocks for items in a collection.
367 selectedId = the id of an object that should be selected, if any
368 collectionName = name of collection
369 fieldName = name of field within models of collection that will be displayed
372 idToOptions = function(selectedId, collectionName, fieldName) {
374 for (index in xos[collectionName].models) {
375 linkedObject = xos[collectionName].models[index];
376 linkedId = linkedObject["id"];
377 linkedName = linkedObject.attributes[fieldName];
378 if (linkedId == selectedId) {
379 selected = " selected";
383 result = result + '<option value="' + linkedId + '"' + selected + '>' + linkedName + '</option>';
388 /* Constructs an html <select> and the <option>s to go with it.
390 variable = variable name to return to form
391 selectedId = the id of an object that should be selected, if any
392 collectionName = name of collection
393 fieldName = name of field within models of collection that will be displayed
396 idToSelect = function(variable, selectedId, collectionName, fieldName) {
397 result = '<select name="' + variable + '">' +
398 idToOptions(selectedId, collectionName, fieldName) +