1 function assert(outcome, description) {
3 console.log(description);
7 function templateFromId(id) {
8 return _.template($(id).html());
11 HTMLView = Marionette.ItemView.extend({
13 this.$el.append(this.options.html);
17 XOSApplication = Marionette.Application.extend({
18 detailBoxId: "#detailBox",
19 errorBoxId: "#errorBox",
20 errorCloseButtonId: "#close-error-box",
21 successBoxId: "#successBox",
22 successCloseButtonId: "#close-success-box",
23 errorTemplate: "#xos-error-template",
24 successTemplate: "#xos-success-template",
27 hideError: function() {
28 if (this.logWindowId) {
30 $(this.errorBoxId).hide();
31 $(this.successBoxId).hide();
35 showSuccess: function(result) {
36 result["success"] = "success";
37 if (this.logTableId) {
38 this.appendLogWindow(result);
40 $(this.successBoxId).show();
41 $(this.successBoxId).html(_.template($(this.successTemplate).html())(result));
43 $(this.successCloseButtonId).unbind().bind('click', function() {
44 $(that.successBoxId).hide();
49 showError: function(result) {
50 result["success"] = "failure";
51 if (this.logTableId) {
52 this.appendLogWindow(result);
54 $(this.errorBoxId).show();
55 $(this.errorBoxId).html(_.template($(this.errorTemplate).html())(result));
57 $(this.errorCloseButtonId).unbind().bind('click', function() {
58 $(that.errorBoxId).hide();
63 showInformational: function(result) {
64 result["success"] = "information";
65 if (this.logTableId) {
66 return this.appendLogWindow(result);
72 appendLogWindow: function(result) {
73 // compute a new logMessageId for this log message
74 logMessageId = "logMessage" + this.logMessageCount;
75 this.logMessageCount = this.logMessageCount + 1;
76 result["logMessageId"] = logMessageId;
78 logMessageTemplate=$("#xos-log-template").html();
79 assert(logMessageTemplate != undefined, "logMessageTemplate is undefined");
80 newRow = _.template(logMessageTemplate, result);
81 assert(newRow != undefined, "newRow is undefined");
83 if (result["infoMsgId"] != undefined) {
84 // We were passed the logMessageId of an informational message,
85 // and the caller wants us to replace that message with our own.
86 // i.e. replace an informational message with a success or an error.
87 $("#"+result["infoMsgId"]).replaceWith(newRow);
89 // Create a brand new log message rather than replacing one.
90 logTableBody = $(this.logTableId + " tbody");
91 logTableBody.prepend(newRow);
94 if (this.statusMsgId) {
95 $(this.statusMsgId).html( templateFromId("#xos-status-template")(result) );
101 hideLinkedItems: function(result) {
104 this["linkedObjs" + (index+1)].empty();
\r
109 listViewShower: function(listViewName, collection_name, regionName, title) {
\r
111 return function() {
\r
112 app[regionName].show(new app[listViewName]);
\r
113 app.hideLinkedItems();
\r
114 $("#contentTitle").html(templateFromId("#xos-title-list")({"title": title}));
\r
115 $("#detail").show();
\r
116 $("#xos-listview-button-box").show();
\r
118 $("#xos-detail-button-box").hide();
\r
122 detailShower: function(detailName, collection_name, regionName, title) {
\r
124 showModelId = function(model_id) {
\r
125 showModel = function(model) {
\r
126 detailViewClass = app[detailName];
\r
127 detailView = new detailViewClass({model: model});
\r
128 app[regionName].show(detailView);
\r
129 detailView.showLinkedItems();
\r
130 $("#xos-detail-button-box").show();
\r
131 $("#xos-listview-button-box").hide();
\r
134 $("#contentTitle").html(templateFromId("#xos-title-detail")({"title": title}));
\r
136 collection = xos[collection_name];
\r
137 model = collection.get(model_id);
\r
138 if (model == undefined) {
\r
139 if (!collection.isLoaded) {
\r
140 // If the model cannot be found, then maybe it's because
\r
141 // we haven't finished loading the collection yet. So wait for
\r
142 // the sort event to complete, then try again.
\r
143 collection.once("sort", function() {
\r
144 collection = xos[collection_name];
\r
145 model = collection.get(model_id);
\r
146 if (model == undefined) {
\r
147 // We tried. It's not here. Complain to the user.
\r
148 app[regionName].show(new HTMLView({html: "failed to load object " + model_id + " from collection " + collection_name}));
\r
154 // The collection was loaded, the user must just be asking for something we don't have.
\r
155 app[regionName].show(new HTMLView({html: "failed to load object " + model_id + " from collection " + collection_name}));
\r
161 return showModelId;
\r
167 app - MarionetteApplication
168 template - template (See XOSHelper.html)
171 XOSDetailView = Marionette.ItemView.extend({
174 events: {"click button.btn-xos-save-continue": "submitContinueClicked",
175 "click button.btn-xos-save-leave": "submitLeaveClicked",
176 "click button.btn-xos-save-another": "submitAddAnotherClicked",
177 "change input": "inputChanged"},
179 /* inputChanged is watching the onChange events of the input controls. We
180 do this to track when this view is 'dirty', so we can throw up a warning
\r
181 if the user tries to change his slices without saving first.
\r
184 inputChanged: function(e) {
\r
188 saveError: function(model, result, xhr, infoMsgId) {
\r
189 result["what"] = "save " + model.__proto__.modelName;
\r
190 result["infoMsgId"] = infoMsgId;
\r
191 this.app.showError(result);
\r
194 saveSuccess: function(model, result, xhr, infoMsgId) {
\r
195 result = {status: xhr.xhr.status, statusText: xhr.xhr.statusText};
\r
196 result["what"] = "save " + model.__proto__.modelName;
\r
197 result["infoMsgId"] = infoMsgId;
\r
198 this.app.showSuccess(result);
\r
201 submitContinueClicked: function(e) {
202 console.log("saveContinue");
207 submitLeaveClicked: function(e) {
208 console.log("saveLeave");
213 submitAddAnotherClicked: function(e) {
214 console.log("saveAnother");
220 this.app.hideError();
221 var infoMsgId = this.app.showInformational( {what: "save " + this.model.__proto__.modelName, status: "", statusText: "in progress..."} );
\r
222 var data = Backbone.Syphon.serialize(this);
\r
224 this.model.save(data, {error: function(model, result, xhr) { that.saveError(model,result,xhr,infoMsgId);},
\r
225 success: function(model, result, xhr) { that.saveSuccess(model,result,xhr,infoMsgId);}});
\r
226 this.dirty = false;
\r
229 tabClick: function(tabId, regionName) {
230 region = this.app[regionName];
\r
231 if (this.currentTabRegion != undefined) {
\r
232 this.currentTabRegion.$el.hide();
\r
234 if (this.currentTabId != undefined) {
\r
235 $(this.currentTabId).removeClass('active');
\r
237 this.currentTabRegion = region;
\r
238 this.currentTabRegion.$el.show();
\r
240 this.currentTabId = tabId;
\r
241 $(tabId).addClass('active');
\r
244 showTabs: function(tabs) {
245 template = templateFromId("#xos-tabs-template", {tabs: tabs});
246 $("#tabs").html(template(tabs));
249 _.each(tabs, function(tab) {
250 var regionName = tab["region"];
251 var tabId = '#xos-nav-'+regionName;
252 $(tabId).bind('click', function() { that.tabClick(tabId, regionName); });
258 showLinkedItems: function() {
261 tabs.push({name: "details", region: "detail"});
264 for (relatedName in this.model.collection.relatedCollections) {
\r
265 relatedField = this.model.collection.relatedCollections[relatedName];
\r
266 regionName = "linkedObjs" + (index+1);
\r
268 relatedListViewClassName = relatedName + "ListView";
\r
269 assert(this.app[relatedListViewClassName] != undefined, relatedListViewClassName + " not found");
\r
270 relatedListViewClass = this.app[relatedListViewClassName].extend({collection: xos[relatedName].filterBy(relatedField,this.model.id)});
\r
271 this.app[regionName].show(new relatedListViewClass());
\r
272 if (this.app.hideTabsByDefault) {
\r
273 this.app[regionName].$el.hide();
\r
275 tabs.push({name: relatedName, region: regionName});
\r
280 this.app["linkedObjs" + (index+1)].empty();
\r
284 this.showTabs(tabs);
\r
285 this.tabClick('#xos-nav-detail', 'detail');
\r
291 This is for items that will be displayed as table rows.
293 app - MarionetteApplication
294 template - template (See XOSHelper.html)
295 detailClass - class of detail view, probably an XOSDetailView
298 XOSItemView = Marionette.ItemView.extend({
300 className: 'test-tablerow',
302 events: {"click": "changeItem"},
304 changeItem: function(e) {
\r
305 this.app.hideError();
\r
306 e.preventDefault();
\r
307 e.stopPropagation();
\r
309 this.app.navigateToModel(this.app, this.detailClass, this.detailNavLink, this.model);
\r
315 app - MarionetteApplication
316 childView - class of ItemView, probably an XOSItemView
317 template - template (see xosHelper.html)
318 collection - collection that holds these objects
319 title - title to display in template
322 XOSListView = Marionette.CompositeView.extend({
323 childViewContainer: 'tbody',
\r
325 initialize: function() {
\r
326 this.listenTo(this.collection, 'change', this._renderChildren)
328 // Because many of the templates use idToName(), we need to
329 // listen to the collections that hold the names for the ids
330 // that we want to display.
331 for (i in this.collection.foreignCollections) {
332 foreignName = this.collection.foreignCollections[i];
333 if (xos[foreignName] == undefined) {
334 console.log("Failed to find xos class " + foreignName);
336 this.listenTo(xos[foreignName], 'change', this._renderChildren);
337 this.listenTo(xos[foreignName], 'sort', this._renderChildren);
341 templateHelpers: function() {
342 return { title: this.title };
346 /* Give an id, the name of a collection, and the name of a field for models
347 within that collection, lookup the id and return the value of the field.
350 idToName = function(id, collectionName, fieldName) {
351 linkedObject = xos[collectionName].get(id);
352 if (linkedObject == undefined) {
355 return linkedObject.attributes[fieldName];
359 /* Constructs lists of <option> html blocks for items in a collection.
361 selectedId = the id of an object that should be selected, if any
362 collectionName = name of collection
363 fieldName = name of field within models of collection that will be displayed
366 idToOptions = function(selectedId, collectionName, fieldName) {
368 for (index in xos[collectionName].models) {
369 linkedObject = xos[collectionName].models[index];
370 linkedId = linkedObject["id"];
371 linkedName = linkedObject.attributes[fieldName];
372 if (linkedId == selectedId) {
373 selected = " selected";
377 result = result + '<option value="' + linkedId + '"' + selected + '>' + linkedName + '</option>';
382 /* Constructs an html <select> and the <option>s to go with it.
384 variable = variable name to return to form
385 selectedId = the id of an object that should be selected, if any
386 collectionName = name of collection
387 fieldName = name of field within models of collection that will be displayed
390 idToSelect = function(variable, selectedId, collectionName, fieldName) {
391 result = '<select name="' + variable + '">' +
392 idToOptions(selectedId, collectionName, fieldName) +