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["success"] = "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["success"] = "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["success"] = "information";
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");
208 submitAddAnotherClicked: function(e) {
209 console.log("saveAnother");
215 this.app.hideError();
216 var infoMsgId = this.app.showInformational( {what: "save " + this.model.__proto__.modelName, status: "", statusText: "in progress..."} );
\r
217 var data = Backbone.Syphon.serialize(this);
\r
219 this.model.save(data, {error: function(model, result, xhr) { that.saveError(model,result,xhr,infoMsgId);},
\r
220 success: function(model, result, xhr) { that.saveSuccess(model,result,xhr,infoMsgId);}});
\r
221 this.dirty = false;
\r
224 tabClick: function(tabId, regionName) {
225 region = this.app[regionName];
\r
226 if (this.currentTabRegion != undefined) {
\r
227 this.currentTabRegion.$el.hide();
\r
229 if (this.currentTabId != undefined) {
\r
230 $(this.currentTabId).removeClass('active');
\r
232 this.currentTabRegion = region;
\r
233 this.currentTabRegion.$el.show();
\r
235 this.currentTabId = tabId;
\r
236 $(tabId).addClass('active');
\r
239 showTabs: function(tabs) {
240 template = templateFromId("#xos-tabs-template", {tabs: tabs});
241 $("#tabs").html(template(tabs));
244 _.each(tabs, function(tab) {
245 var regionName = tab["region"];
246 var tabId = '#xos-nav-'+regionName;
247 $(tabId).bind('click', function() { that.tabClick(tabId, regionName); });
253 showLinkedItems: function() {
256 tabs.push({name: "details", region: "detail"});
259 for (relatedName in this.model.collection.relatedCollections) {
\r
260 relatedField = this.model.collection.relatedCollections[relatedName];
\r
261 regionName = "linkedObjs" + (index+1);
\r
263 relatedListViewClassName = relatedName + "ListView";
\r
264 assert(this.app[relatedListViewClassName] != undefined, relatedListViewClassName + " not found");
\r
265 relatedListViewClass = this.app[relatedListViewClassName].extend({collection: xos[relatedName].filterBy(relatedField,this.model.id)});
\r
266 this.app[regionName].show(new relatedListViewClass());
\r
267 if (this.app.hideTabsByDefault) {
\r
268 this.app[regionName].$el.hide();
\r
270 tabs.push({name: relatedName, region: regionName});
\r
275 this.app["linkedObjs" + (index+1)].empty();
\r
279 this.showTabs(tabs);
\r
280 this.tabClick('#xos-nav-detail', 'detail');
\r
286 This is for items that will be displayed as table rows.
288 app - MarionetteApplication
289 template - template (See XOSHelper.html)
290 detailClass - class of detail view, probably an XOSDetailView
293 XOSItemView = Marionette.ItemView.extend({
295 className: 'test-tablerow',
297 events: {"click": "changeItem"},
299 changeItem: function(e) {
\r
300 this.app.hideError();
\r
301 e.preventDefault();
\r
302 e.stopPropagation();
\r
304 this.app.navigateToModel(this.app, this.detailClass, this.detailNavLink, this.model);
\r
310 app - MarionetteApplication
311 childView - class of ItemView, probably an XOSItemView
312 template - template (see xosHelper.html)
313 collection - collection that holds these objects
314 title - title to display in template
317 XOSListView = Marionette.CompositeView.extend({
318 childViewContainer: 'tbody',
\r
320 events: {"click button.btn-xos-add": "addClicked",
\r
323 addClicked: function(e) {
326 this.app.Router.navigate("add" + firstCharUpper(this.collection.modelName), {trigger: true});
329 initialize: function() {
\r
330 this.listenTo(this.collection, 'change', this._renderChildren)
332 // Because many of the templates use idToName(), we need to
333 // listen to the collections that hold the names for the ids
334 // that we want to display.
335 for (i in this.collection.foreignCollections) {
336 foreignName = this.collection.foreignCollections[i];
337 if (xos[foreignName] == undefined) {
338 console.log("Failed to find xos class " + foreignName);
340 this.listenTo(xos[foreignName], 'change', this._renderChildren);
341 this.listenTo(xos[foreignName], 'sort', this._renderChildren);
345 templateHelpers: function() {
346 return { title: this.title };
350 /* Give an id, the name of a collection, and the name of a field for models
351 within that collection, lookup the id and return the value of the field.
354 idToName = function(id, collectionName, fieldName) {
355 linkedObject = xos[collectionName].get(id);
356 if (linkedObject == undefined) {
359 return linkedObject.attributes[fieldName];
363 /* Constructs lists of <option> html blocks for items in a collection.
365 selectedId = the id of an object that should be selected, if any
366 collectionName = name of collection
367 fieldName = name of field within models of collection that will be displayed
370 idToOptions = function(selectedId, collectionName, fieldName) {
372 for (index in xos[collectionName].models) {
373 linkedObject = xos[collectionName].models[index];
374 linkedId = linkedObject["id"];
375 linkedName = linkedObject.attributes[fieldName];
376 if (linkedId == selectedId) {
377 selected = " selected";
381 result = result + '<option value="' + linkedId + '"' + selected + '>' + linkedName + '</option>';
386 /* Constructs an html <select> and the <option>s to go with it.
388 variable = variable name to return to form
389 selectedId = the id of an object that should be selected, if any
390 collectionName = name of collection
391 fieldName = name of field within models of collection that will be displayed
394 idToSelect = function(variable, selectedId, collectionName, fieldName) {
395 result = '<select name="' + variable + '">' +
396 idToOptions(selectedId, collectionName, fieldName) +