Scott Baker | 0bf96b2 | 2014-11-04 15:41:47 -0800 | [diff] [blame] | 1 | HTMLView = Marionette.ItemView.extend({ |
| 2 | render: function() { |
| 3 | this.$el.append(this.options.html); |
| 4 | }, |
| 5 | }); |
| 6 | |
Scott Baker | f50222e | 2014-12-08 14:49:03 -0800 | [diff] [blame] | 7 | FilteredCompositeView = Marionette.CompositeView.extend( { |
| 8 | showCollection: function() { |
| 9 | var ChildView; |
| 10 | this.collection.each(function(child, index) { |
| 11 | if (this.filter && !this.filter(child)) { |
| 12 | return; |
| 13 | } |
| 14 | ChildView = this.getChildView(child); |
| 15 | this.addChild(child, ChildView, index); |
| 16 | }, this); |
| 17 | |
| 18 | }, |
| 19 | }); |
| 20 | |
Scott Baker | 29e8a2c | 2014-12-02 17:59:02 -0800 | [diff] [blame] | 21 | XOSRouter = Marionette.AppRouter.extend({ |
| 22 | initialize: function() {
|
| 23 | this.routeStack=[];
|
| 24 | },
|
| 25 |
|
| 26 | onRoute: function(x,y,z) {
|
| 27 | this.routeStack.push(Backbone.history.fragment);
|
| 28 | },
|
| 29 |
|
| 30 | prevPage: function() {
|
Scott Baker | f50222e | 2014-12-08 14:49:03 -0800 | [diff] [blame] | 31 | return this.routeStack.slice(-1)[0]; |
Scott Baker | 29e8a2c | 2014-12-02 17:59:02 -0800 | [diff] [blame] | 32 | }, |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 33 | |
| 34 | showPreviousURL: function() { |
| 35 | prevPage = this.prevPage(); |
Scott Baker | f50222e | 2014-12-08 14:49:03 -0800 | [diff] [blame] | 36 | console.log("showPreviousURL"); |
| 37 | console.log(this.routeStack); |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 38 | if (prevPage) { |
| 39 | this.navigate("#"+prevPage, {trigger: false, replace: true} ); |
| 40 | } |
| 41 | }, |
Scott Baker | ab5f136 | 2014-12-09 19:39:45 -0800 | [diff] [blame] | 42 | |
| 43 | navigate: function(href, options) { |
| 44 | if (options.force) { |
| 45 | Marionette.AppRouter.prototype.navigate.call(this, "nowhere", {trigger: false, replace: true}); |
| 46 | } |
| 47 | Marionette.AppRouter.prototype.navigate.call(this, href, options); |
| 48 | }, |
Scott Baker | 29e8a2c | 2014-12-02 17:59:02 -0800 | [diff] [blame] | 49 | });
|
| 50 | |
| 51 | |
| 52 | |
Scott Baker | fdaee92 | 2014-11-03 09:43:23 -0800 | [diff] [blame] | 53 | XOSApplication = Marionette.Application.extend({ |
Scott Baker | c16b4c1 | 2014-11-03 23:54:24 -0800 | [diff] [blame] | 54 | detailBoxId: "#detailBox", |
Scott Baker | fdaee92 | 2014-11-03 09:43:23 -0800 | [diff] [blame] | 55 | errorBoxId: "#errorBox", |
| 56 | errorCloseButtonId: "#close-error-box", |
| 57 | successBoxId: "#successBox", |
| 58 | successCloseButtonId: "#close-success-box", |
| 59 | errorTemplate: "#xos-error-template", |
| 60 | successTemplate: "#xos-success-template", |
Scott Baker | 0bf96b2 | 2014-11-04 15:41:47 -0800 | [diff] [blame] | 61 | logMessageCount: 0, |
Scott Baker | fdaee92 | 2014-11-03 09:43:23 -0800 | [diff] [blame] | 62 | |
Scott Baker | 5f9e767 | 2014-12-02 12:13:49 -0800 | [diff] [blame] | 63 | confirmDialog: function(view, event, callback) { |
Scott Baker | 1e87c5a | 2014-11-18 23:31:48 -0800 | [diff] [blame] | 64 | $("#xos-confirm-dialog").dialog({ |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 65 | autoOpen: false, |
| 66 | modal: true, |
| 67 | buttons : { |
| 68 | "Confirm" : function() { |
| 69 | $(this).dialog("close"); |
| 70 | if (event) { |
| 71 | view.trigger(event); |
| 72 | } |
| 73 | if (callback) { |
| 74 | callback(); |
| 75 | } |
| 76 | }, |
| 77 | "Cancel" : function() { |
| 78 | $(this).dialog("close"); |
| 79 | } |
| 80 | } |
Scott Baker | 1e87c5a | 2014-11-18 23:31:48 -0800 | [diff] [blame] | 81 | }); |
| 82 | $("#xos-confirm-dialog").dialog("open"); |
| 83 | }, |
| 84 | |
Scott Baker | 562d595 | 2014-11-24 23:26:12 -0800 | [diff] [blame] | 85 | popupErrorDialog: function(responseText) { |
Scott Baker | 9215275 | 2014-12-01 17:06:31 -0800 | [diff] [blame] | 86 | try { |
| 87 | parsed_error=$.parseJSON(responseText); |
| 88 | width=300; |
| 89 | } |
| 90 | catch(err) { |
| 91 | parsed_error=undefined; |
| 92 | width=640; // django stacktraces like wide width |
| 93 | } |
| 94 | if (parsed_error) { |
Scott Baker | 5f9e767 | 2014-12-02 12:13:49 -0800 | [diff] [blame] | 95 | $("#xos-error-dialog").html(templateFromId("#xos-error-response")(parsed_error)); |
Scott Baker | 9215275 | 2014-12-01 17:06:31 -0800 | [diff] [blame] | 96 | } else { |
| 97 | $("#xos-error-dialog").html(templateFromId("#xos-error-rawresponse")({responseText: responseText})) |
| 98 | } |
| 99 | |
Scott Baker | 562d595 | 2014-11-24 23:26:12 -0800 | [diff] [blame] | 100 | $("#xos-error-dialog").dialog({ |
| 101 | modal: true, |
Scott Baker | 9215275 | 2014-12-01 17:06:31 -0800 | [diff] [blame] | 102 | width: width, |
Scott Baker | 562d595 | 2014-11-24 23:26:12 -0800 | [diff] [blame] | 103 | buttons: { |
| 104 | Ok: function() { $(this).dialog("close"); } |
| 105 | } |
| 106 | }); |
| 107 | }, |
| 108 | |
Scott Baker | 0bf96b2 | 2014-11-04 15:41:47 -0800 | [diff] [blame] | 109 | hideLinkedItems: function(result) { |
Scott Baker | 9d37d56 | 2014-11-04 23:20:48 -0800 | [diff] [blame] | 110 | var index=0; |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 111 | while (index<4) { |
| 112 | this["linkedObjs" + (index+1)].empty(); |
| 113 | index = index + 1; |
| 114 | } |
| 115 | }, |
| 116 | |
Scott Baker | ab5f136 | 2014-12-09 19:39:45 -0800 | [diff] [blame] | 117 | hideTabs: function() { $("#tabs").hide(); }, |
| 118 | showTabs: function() { $("#tabs").show(); }, |
| 119 | |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 120 | createListHandler: function(listViewName, collection_name, regionName, title) { |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 121 | var app=this; |
| 122 | return function() { |
Scott Baker | ca4bf92 | 2014-12-09 18:38:13 -0800 | [diff] [blame] | 123 | listView = new app[listViewName]; |
| 124 | app[regionName].show(listView); |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 125 | app.hideLinkedItems(); |
| 126 | $("#contentTitle").html(templateFromId("#xos-title-list")({"title": title})); |
| 127 | $("#detail").show(); |
Scott Baker | ab5f136 | 2014-12-09 19:39:45 -0800 | [diff] [blame] | 128 | app.hideTabs(); |
Scott Baker | ca4bf92 | 2014-12-09 18:38:13 -0800 | [diff] [blame] | 129 | |
| 130 | listButtons = new XOSListButtonView({linkedView: listView}); |
| 131 | app["rightButtonPanel"].show(listButtons); |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 132 | } |
| 133 | }, |
| 134 | |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 135 | createAddHandler: function(detailName, collection_name, regionName, title) { |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 136 | var app=this; |
| 137 | return function() { |
Scott Baker | ab5f136 | 2014-12-09 19:39:45 -0800 | [diff] [blame] | 138 | console.log("addHandler"); |
| 139 | |
| 140 | app.hideLinkedItems(); |
| 141 | app.hideTabs(); |
| 142 | |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 143 | model = new xos[collection_name].model(); |
| 144 | detailViewClass = app[detailName]; |
| 145 | detailView = new detailViewClass({model: model, collection:xos[collection_name]}); |
| 146 | app[regionName].show(detailView); |
Scott Baker | ab5f136 | 2014-12-09 19:39:45 -0800 | [diff] [blame] | 147 | |
| 148 | detailButtons = new XOSDetailButtonView({linkedView: detailView}); |
| 149 | app["rightButtonPanel"].show(detailButtons); |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 150 | } |
| 151 | }, |
| 152 | |
Scott Baker | 0a636cb | 2014-12-07 22:27:09 -0800 | [diff] [blame] | 153 | createAddChildHandler: function(addChildName, collection_name) { |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 154 | var app=this; |
| 155 | return function(parent_modelName, parent_fieldName, parent_id) { |
| 156 | app.Router.showPreviousURL(); |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 157 | model = new xos[collection_name].model(); |
| 158 | model.attributes[parent_fieldName] = parent_id; |
Scott Baker | 07b4a25 | 2014-12-08 23:54:18 -0800 | [diff] [blame] | 159 | model.readOnlyFields.push(parent_fieldName); |
Scott Baker | 0a636cb | 2014-12-07 22:27:09 -0800 | [diff] [blame] | 160 | detailViewClass = app[addChildName]; |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 161 | var detailView = new detailViewClass({model: model, collection:xos[collection_name]}); |
| 162 | detailView.dialog = $("xos-addchild-dialog"); |
| 163 | app["addChildDetail"].show(detailView); |
| 164 | $("#xos-addchild-dialog").dialog({ |
| 165 | autoOpen: false, |
| 166 | modal: true, |
| 167 | width: 640, |
| 168 | buttons : { |
| 169 | "Save" : function() { |
Scott Baker | 0a636cb | 2014-12-07 22:27:09 -0800 | [diff] [blame] | 170 | var addDialog = this; |
| 171 | detailView.synchronous = true; |
Scott Baker | ab5f136 | 2014-12-09 19:39:45 -0800 | [diff] [blame] | 172 | detailView.afterSave = function() { console.log("addChild afterSave"); $(addDialog).dialog("close"); } |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 173 | detailView.save(); |
| 174 | |
Scott Baker | 0a636cb | 2014-12-07 22:27:09 -0800 | [diff] [blame] | 175 | //$(this).dialog("close"); |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 176 | }, |
| 177 | "Cancel" : function() { |
| 178 | $(this).dialog("close"); |
| 179 | } |
| 180 | } |
| 181 | }); |
| 182 | $("#xos-addchild-dialog").dialog("open"); |
| 183 | } |
| 184 | }, |
| 185 | |
| 186 | createDeleteHandler: function(collection_name) { |
Scott Baker | 29e8a2c | 2014-12-02 17:59:02 -0800 | [diff] [blame] | 187 | var app=this; |
| 188 | return function(model_id) { |
| 189 | console.log("deleteCalled"); |
| 190 | collection = xos[collection_name]; |
| 191 | model = collection.get(model_id); |
| 192 | assert(model!=undefined, "failed to get model " + model_id + " from collection " + collection_name); |
Scott Baker | f50222e | 2014-12-08 14:49:03 -0800 | [diff] [blame] | 193 | app.Router.showPreviousURL(); |
| 194 | app.deleteDialog(model); |
Scott Baker | 29e8a2c | 2014-12-02 17:59:02 -0800 | [diff] [blame] | 195 | } |
| 196 | }, |
| 197 | |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 198 | createDetailHandler: function(detailName, collection_name, regionName, title) { |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 199 | var app=this; |
| 200 | showModelId = function(model_id) { |
| 201 | $("#contentTitle").html(templateFromId("#xos-title-detail")({"title": title})); |
| 202 | |
| 203 | collection = xos[collection_name]; |
| 204 | model = collection.get(model_id); |
| 205 | if (model == undefined) { |
| 206 | app[regionName].show(new HTMLView({html: "failed to load object " + model_id + " from collection " + collection_name})); |
| 207 | } else { |
| 208 | detailViewClass = app[detailName]; |
| 209 | detailView = new detailViewClass({model: model}); |
| 210 | app[regionName].show(detailView); |
| 211 | detailView.showLinkedItems(); |
Scott Baker | ca4bf92 | 2014-12-09 18:38:13 -0800 | [diff] [blame] | 212 | |
| 213 | detailButtons = new XOSDetailButtonView({linkedView: detailView}); |
| 214 | app["rightButtonPanel"].show(detailButtons); |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 215 | } |
| 216 | } |
| 217 | return showModelId; |
| 218 | }, |
| 219 | |
| 220 | /* error handling callbacks */ |
| 221 | |
| 222 | hideError: function() { |
Scott Baker | 5f9e767 | 2014-12-02 12:13:49 -0800 | [diff] [blame] | 223 | if (this.logWindowId) { |
| 224 | } else { |
| 225 | $(this.errorBoxId).hide(); |
| 226 | $(this.successBoxId).hide(); |
| 227 | } |
| 228 | }, |
Scott Baker | fdaee92 | 2014-11-03 09:43:23 -0800 | [diff] [blame] | 229 | |
Scott Baker | 5f9e767 | 2014-12-02 12:13:49 -0800 | [diff] [blame] | 230 | showSuccess: function(result) { |
| 231 | result["statusclass"] = "success"; |
| 232 | if (this.logTableId) { |
| 233 | this.appendLogWindow(result); |
| 234 | } else { |
| 235 | $(this.successBoxId).show(); |
| 236 | $(this.successBoxId).html(_.template($(this.successTemplate).html())(result)); |
| 237 | var that=this; |
| 238 | $(this.successCloseButtonId).unbind().bind('click', function() { |
| 239 | $(that.successBoxId).hide(); |
| 240 | }); |
| 241 | } |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 242 | }, |
| 243 | |
| 244 | showError: function(result) { |
Scott Baker | 5f9e767 | 2014-12-02 12:13:49 -0800 | [diff] [blame] | 245 | result["statusclass"] = "failure"; |
| 246 | if (this.logTableId) { |
| 247 | this.appendLogWindow(result); |
| 248 | this.popupErrorDialog(result.responseText); |
| 249 | } else { |
| 250 | // this is really old stuff |
| 251 | $(this.errorBoxId).show(); |
| 252 | $(this.errorBoxId).html(_.template($(this.errorTemplate).html())(result)); |
| 253 | var that=this; |
| 254 | $(this.errorCloseButtonId).unbind().bind('click', function() { |
| 255 | $(that.errorBoxId).hide(); |
| 256 | }); |
| 257 | } |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 258 | }, |
| 259 | |
| 260 | showInformational: function(result) { |
Scott Baker | 5f9e767 | 2014-12-02 12:13:49 -0800 | [diff] [blame] | 261 | result["statusclass"] = "inprog"; |
| 262 | if (this.logTableId) { |
| 263 | return this.appendLogWindow(result); |
| 264 | } else { |
| 265 | return undefined; |
| 266 | } |
| 267 | }, |
| 268 | |
| 269 | appendLogWindow: function(result) { |
| 270 | // compute a new logMessageId for this log message |
| 271 | logMessageId = "logMessage" + this.logMessageCount; |
| 272 | this.logMessageCount = this.logMessageCount + 1; |
| 273 | result["logMessageId"] = logMessageId; |
| 274 | |
| 275 | logMessageTemplate=$("#xos-log-template").html(); |
| 276 | assert(logMessageTemplate != undefined, "logMessageTemplate is undefined"); |
| 277 | newRow = _.template(logMessageTemplate, result); |
| 278 | assert(newRow != undefined, "newRow is undefined"); |
| 279 | |
| 280 | if (result["infoMsgId"] != undefined) { |
| 281 | // We were passed the logMessageId of an informational message, |
| 282 | // and the caller wants us to replace that message with our own. |
| 283 | // i.e. replace an informational message with a success or an error. |
| 284 | $("#"+result["infoMsgId"]).replaceWith(newRow); |
| 285 | } else { |
| 286 | // Create a brand new log message rather than replacing one. |
| 287 | logTableBody = $(this.logTableId + " tbody"); |
| 288 | logTableBody.prepend(newRow); |
| 289 | } |
| 290 | |
| 291 | if (this.statusMsgId) { |
| 292 | $(this.statusMsgId).html( templateFromId("#xos-status-template")(result) ); |
| 293 | } |
| 294 | |
| 295 | limitTableRows(this.logTableId, 5); |
| 296 | |
| 297 | return logMessageId; |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 298 | }, |
| 299 | |
| 300 | saveError: function(model, result, xhr, infoMsgId) { |
| 301 | console.log("saveError"); |
| 302 | result["what"] = "save " + model.modelName + " " + model.attributes.humanReadableName; |
| 303 | result["infoMsgId"] = infoMsgId; |
| 304 | this.showError(result); |
| 305 | }, |
| 306 | |
| 307 | saveSuccess: function(model, result, xhr, infoMsgId, addToCollection) { |
| 308 | console.log("saveSuccess"); |
Scott Baker | 29e8a2c | 2014-12-02 17:59:02 -0800 | [diff] [blame] | 309 | if (model.addToCollection) { |
| 310 | console.log("addToCollection"); |
Scott Baker | f50222e | 2014-12-08 14:49:03 -0800 | [diff] [blame] | 311 | console.log(model.addToCollection); |
Scott Baker | 29e8a2c | 2014-12-02 17:59:02 -0800 | [diff] [blame] | 312 | model.addToCollection.add(model); |
| 313 | model.addToCollection.sort(); |
| 314 | model.addToCollection = undefined; |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 315 | } |
| 316 | result = {status: xhr.xhr.status, statusText: xhr.xhr.statusText}; |
| 317 | result["what"] = "save " + model.modelName + " " + model.attributes.humanReadableName; |
| 318 | result["infoMsgId"] = infoMsgId; |
| 319 | this.showSuccess(result); |
Scott Baker | 5f9e767 | 2014-12-02 12:13:49 -0800 | [diff] [blame] | 320 | }, |
| 321 | |
| 322 | destroyError: function(model, result, xhr, infoMsgId) { |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 323 | result["what"] = "destroy " + model.modelName + " " + model.attributes.humanReadableName; |
| 324 | result["infoMsgId"] = infoMsgId; |
| 325 | this.showError(result); |
| 326 | }, |
| 327 | |
| 328 | destroySuccess: function(model, result, xhr, infoMsgId) { |
| 329 | result = {status: xhr.xhr.status, statusText: xhr.xhr.statusText}; |
| 330 | result["what"] = "destroy " + model.modelName + " " + model.attributes.humanReadableName; |
| 331 | result["infoMsgId"] = infoMsgId; |
| 332 | this.showSuccess(result); |
| 333 | }, |
| 334 | |
| 335 | /* end error handling callbacks */ |
| 336 | |
| 337 | destroyModel: function(model) { |
| 338 | //console.log("destroyModel"); console.log(model); |
Scott Baker | 5f9e767 | 2014-12-02 12:13:49 -0800 | [diff] [blame] | 339 | this.hideError(); |
| 340 | var infoMsgId = this.showInformational( {what: "destroy " + model.modelName + " " + model.attributes.humanReadableName, status: "", statusText: "in progress..."} ); |
| 341 | var that = this; |
| 342 | model.destroy({error: function(model, result, xhr) { that.destroyError(model,result,xhr,infoMsgId);}, |
| 343 | success: function(model, result, xhr) { that.destroySuccess(model,result,xhr,infoMsgId);}}); |
| 344 | }, |
| 345 | |
Scott Baker | 29e8a2c | 2014-12-02 17:59:02 -0800 | [diff] [blame] | 346 | deleteDialog: function(model, afterDelete) { |
Scott Baker | 5f9e767 | 2014-12-02 12:13:49 -0800 | [diff] [blame] | 347 | var that=this; |
Scott Baker | 29e8a2c | 2014-12-02 17:59:02 -0800 | [diff] [blame] | 348 | assert(model!=undefined, "deleteDialog's model is undefined"); |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 349 | //console.log("deleteDialog"); console.log(model); |
| 350 | this.confirmDialog(null, null, function() { |
| 351 | //console.log("deleteConfirm"); console.log(model); |
Scott Baker | 5f9e767 | 2014-12-02 12:13:49 -0800 | [diff] [blame] | 352 | modelName = model.modelName; |
| 353 | that.destroyModel(model); |
Scott Baker | 29e8a2c | 2014-12-02 17:59:02 -0800 | [diff] [blame] | 354 | if (afterDelete=="list") { |
Scott Baker | 5f9e767 | 2014-12-02 12:13:49 -0800 | [diff] [blame] | 355 | that.navigate("list", modelName); |
| 356 | } |
Scott Baker | 5f9e767 | 2014-12-02 12:13:49 -0800 | [diff] [blame] | 357 | }); |
| 358 | }, |
| 359 | }); |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 360 | |
Scott Baker | ca4bf92 | 2014-12-09 18:38:13 -0800 | [diff] [blame] | 361 | XOSButtonView = Marionette.ItemView.extend({ |
| 362 | events: {"click button.btn-xos-save-continue": "submitContinueClicked", |
| 363 | "click button.btn-xos-save-leave": "submitLeaveClicked", |
| 364 | "click button.btn-xos-save-another": "submitAddAnotherClicked", |
| 365 | "click button.btn-xos-delete": "deleteClicked", |
| 366 | "click button.btn-xos-add": "addClicked", |
| 367 | "click button.btn-xos-refresh": "refreshClicked", |
| 368 | }, |
| 369 | |
| 370 | submitLeaveClicked: function(e) { |
| 371 | this.options.linkedView.submitLeaveClicked.call(this.options.linkedView, e); |
| 372 | }, |
| 373 | |
| 374 | submitContinueClicked: function(e) { |
| 375 | this.options.linkedView.submitContinueClicked.call(this.options.linkedView, e); |
| 376 | }, |
| 377 | |
| 378 | submitAddAnotherClicked: function(e) { |
| 379 | this.options.linkedView.submitAddAnotherClicked.call(this.options.linkedView, e); |
| 380 | }, |
| 381 | |
| 382 | submitDeleteClicked: function(e) { |
| 383 | this.options.linkedView.submitDeleteClicked.call(this.options.linkedView, e); |
| 384 | }, |
| 385 | |
| 386 | addClicked: function(e) { |
| 387 | this.options.linkedView.addClicked.call(this.options.linkedView, e); |
| 388 | }, |
| 389 | |
| 390 | refreshClicked: function(e) { |
| 391 | this.options.linkedView.refreshClicked.call(this.options.linkedView, e); |
| 392 | }, |
| 393 | }); |
| 394 | |
| 395 | XOSDetailButtonView = XOSButtonView.extend({ template: "#xos-savebuttons-template" }); |
| 396 | XOSListButtonView = XOSButtonView.extend({ template: "#xos-listbuttons-template" }); |
| 397 | |
Scott Baker | c16b4c1 | 2014-11-03 23:54:24 -0800 | [diff] [blame] | 398 | /* XOSDetailView |
| 399 | extend with: |
| 400 | app - MarionetteApplication |
| 401 | template - template (See XOSHelper.html) |
| 402 | */ |
| 403 | |
| 404 | XOSDetailView = Marionette.ItemView.extend({ |
| 405 | tagName: "div", |
| 406 | |
Scott Baker | e49f08c | 2014-11-07 13:01:43 -0800 | [diff] [blame] | 407 | events: {"click button.btn-xos-save-continue": "submitContinueClicked", |
| 408 | "click button.btn-xos-save-leave": "submitLeaveClicked", |
| 409 | "click button.btn-xos-save-another": "submitAddAnotherClicked", |
Scott Baker | 1e87c5a | 2014-11-18 23:31:48 -0800 | [diff] [blame] | 410 | "click button.btn-xos-delete": "deleteClicked", |
Scott Baker | c16b4c1 | 2014-11-03 23:54:24 -0800 | [diff] [blame] | 411 | "change input": "inputChanged"}, |
| 412 | |
Scott Baker | c16b4c1 | 2014-11-03 23:54:24 -0800 | [diff] [blame] | 413 | /* inputChanged is watching the onChange events of the input controls. We |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 414 | do this to track when this view is 'dirty', so we can throw up a warning |
| 415 | if the user tries to change his slices without saving first. |
| 416 | */ |
| 417 | |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 418 | initialize: function() { |
Scott Baker | f50222e | 2014-12-08 14:49:03 -0800 | [diff] [blame] | 419 | this.on("saveSuccess", this.onAfterSave); |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 420 | this.synchronous = false; |
| 421 | }, |
| 422 | |
| 423 | afterSave: function(e) { |
| 424 | }, |
| 425 | |
Scott Baker | f50222e | 2014-12-08 14:49:03 -0800 | [diff] [blame] | 426 | onAfterSave: function(e) { |
| 427 | this.afterSave(e); |
| 428 | }, |
| 429 | |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 430 | inputChanged: function(e) { |
| 431 | this.dirty = true; |
| 432 | }, |
| 433 | |
| 434 | submitContinueClicked: function(e) { |
Scott Baker | e49f08c | 2014-11-07 13:01:43 -0800 | [diff] [blame] | 435 | console.log("saveContinue"); |
| 436 | e.preventDefault(); |
Scott Baker | cb90181 | 2014-12-09 17:27:52 -0800 | [diff] [blame] | 437 | this.afterSave = function() { }; |
Scott Baker | e49f08c | 2014-11-07 13:01:43 -0800 | [diff] [blame] | 438 | this.save(); |
| 439 | }, |
| 440 | |
| 441 | submitLeaveClicked: function(e) { |
| 442 | console.log("saveLeave"); |
| 443 | e.preventDefault(); |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 444 | var that=this; |
| 445 | this.afterSave = function() { |
| 446 | that.app.navigate("list", that.model.modelName); |
| 447 | } |
Scott Baker | e49f08c | 2014-11-07 13:01:43 -0800 | [diff] [blame] | 448 | this.save(); |
| 449 | }, |
| 450 | |
| 451 | submitAddAnotherClicked: function(e) { |
| 452 | console.log("saveAnother"); |
Scott Baker | ca4bf92 | 2014-12-09 18:38:13 -0800 | [diff] [blame] | 453 | console.log(this); |
Scott Baker | e49f08c | 2014-11-07 13:01:43 -0800 | [diff] [blame] | 454 | e.preventDefault(); |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 455 | var that=this; |
| 456 | this.afterSave = function() { |
Scott Baker | ab5f136 | 2014-12-09 19:39:45 -0800 | [diff] [blame] | 457 | console.log("addAnother afterSave"); |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 458 | that.app.navigate("add", that.model.modelName); |
| 459 | } |
Scott Baker | e49f08c | 2014-11-07 13:01:43 -0800 | [diff] [blame] | 460 | this.save(); |
| 461 | }, |
| 462 | |
| 463 | save: function() { |
| 464 | this.app.hideError(); |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 465 | var data = Backbone.Syphon.serialize(this); |
| 466 | var that = this; |
| 467 | var isNew = !this.model.id; |
| 468 | |
| 469 | this.$el.find(".help-inline").remove(); |
| 470 | |
| 471 | /* although model.validate() is called automatically by |
| 472 | model.save, we call it ourselves, so we can throw up our |
| 473 | validation error before creating the infoMsg in the log |
| 474 | */ |
| 475 | errors = this.model.xosValidate(data); |
| 476 | if (errors) { |
| 477 | this.onFormDataInvalid(errors); |
| 478 | return; |
| 479 | } |
| 480 | |
| 481 | if (isNew) { |
| 482 | this.model.attributes.humanReadableName = "new " + model.modelName; |
| 483 | this.model.addToCollection = this.collection; |
| 484 | } else { |
| 485 | this.model.addToCollection = undefined; |
| 486 | } |
| 487 | |
| 488 | var infoMsgId = this.app.showInformational( {what: "save " + model.modelName + " " + model.attributes.humanReadableName, status: "", statusText: "in progress..."} ); |
| 489 | |
| 490 | this.model.save(data, {error: function(model, result, xhr) { that.app.saveError(model,result,xhr,infoMsgId);}, |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 491 | success: function(model, result, xhr) { that.app.saveSuccess(model,result,xhr,infoMsgId); |
| 492 | if (that.synchronous) { |
| 493 | that.trigger("saveSuccess"); |
| 494 | } |
| 495 | }}); |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 496 | this.dirty = false; |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 497 | |
| 498 | if (!this.synchronous) { |
| 499 | this.afterSave(); |
| 500 | } |
Scott Baker | c16b4c1 | 2014-11-03 23:54:24 -0800 | [diff] [blame] | 501 | }, |
| 502 | |
Scott Baker | 6c19f24 | 2014-12-04 17:23:01 -0800 | [diff] [blame] | 503 | deleteClicked: function(e) { |
| 504 | e.preventDefault(); |
| 505 | this.app.deleteDialog(this.model, "list"); |
Scott Baker | 1e87c5a | 2014-11-18 23:31:48 -0800 | [diff] [blame] | 506 | }, |
| 507 | |
Scott Baker | 9d37d56 | 2014-11-04 23:20:48 -0800 | [diff] [blame] | 508 | tabClick: function(tabId, regionName) { |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 509 | region = this.app[regionName]; |
| 510 | if (this.currentTabRegion != undefined) { |
| 511 | this.currentTabRegion.$el.hide(); |
| 512 | } |
| 513 | if (this.currentTabId != undefined) { |
| 514 | $(this.currentTabId).removeClass('active'); |
| 515 | } |
| 516 | this.currentTabRegion = region; |
| 517 | this.currentTabRegion.$el.show(); |
| 518 | |
| 519 | this.currentTabId = tabId; |
| 520 | $(tabId).addClass('active'); |
Scott Baker | 9d37d56 | 2014-11-04 23:20:48 -0800 | [diff] [blame] | 521 | }, |
| 522 | |
| 523 | showTabs: function(tabs) { |
| 524 | template = templateFromId("#xos-tabs-template", {tabs: tabs}); |
| 525 | $("#tabs").html(template(tabs)); |
| 526 | var that = this; |
| 527 | |
| 528 | _.each(tabs, function(tab) { |
| 529 | var regionName = tab["region"]; |
| 530 | var tabId = '#xos-nav-'+regionName; |
| 531 | $(tabId).bind('click', function() { that.tabClick(tabId, regionName); }); |
| 532 | }); |
| 533 | |
| 534 | $("#tabs").show(); |
| 535 | }, |
| 536 | |
Scott Baker | c16b4c1 | 2014-11-03 23:54:24 -0800 | [diff] [blame] | 537 | showLinkedItems: function() { |
Scott Baker | 9d37d56 | 2014-11-04 23:20:48 -0800 | [diff] [blame] | 538 | tabs=[]; |
| 539 | |
| 540 | tabs.push({name: "details", region: "detail"}); |
| 541 | |
Scott Baker | cb90181 | 2014-12-09 17:27:52 -0800 | [diff] [blame] | 542 | makeFilter = function(relatedField, relatedId) { |
| 543 | return function(model) { return model.attributes[relatedField] == relatedId; } |
| 544 | }; |
| 545 | |
Scott Baker | 9d37d56 | 2014-11-04 23:20:48 -0800 | [diff] [blame] | 546 | var index=0; |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 547 | for (relatedName in this.model.collection.relatedCollections) { |
Scott Baker | f50222e | 2014-12-08 14:49:03 -0800 | [diff] [blame] | 548 | var relatedField = this.model.collection.relatedCollections[relatedName]; |
| 549 | var relatedId = this.model.id; |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 550 | regionName = "linkedObjs" + (index+1); |
| 551 | |
| 552 | relatedListViewClassName = relatedName + "ListView"; |
| 553 | assert(this.app[relatedListViewClassName] != undefined, relatedListViewClassName + " not found"); |
Scott Baker | f50222e | 2014-12-08 14:49:03 -0800 | [diff] [blame] | 554 | relatedListViewClass = this.app[relatedListViewClassName].extend({collection: xos[relatedName], |
Scott Baker | ca4bf92 | 2014-12-09 18:38:13 -0800 | [diff] [blame] | 555 | filter: makeFilter(relatedField, relatedId), |
Scott Baker | 07cdef0 | 2014-12-08 11:13:43 -0800 | [diff] [blame] | 556 | parentModel: this.model}); |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 557 | this.app[regionName].show(new relatedListViewClass()); |
| 558 | if (this.app.hideTabsByDefault) { |
| 559 | this.app[regionName].$el.hide(); |
| 560 | } |
| 561 | tabs.push({name: relatedName, region: regionName}); |
| 562 | index = index + 1; |
| 563 | } |
| 564 | |
| 565 | while (index<4) { |
| 566 | this.app["linkedObjs" + (index+1)].empty(); |
| 567 | index = index + 1; |
| 568 | } |
| 569 | |
| 570 | this.showTabs(tabs); |
| 571 | this.tabClick('#xos-nav-detail', 'detail'); |
| 572 | }, |
| 573 | |
| 574 | onFormDataInvalid: function(errors) { |
| 575 | var self=this; |
| 576 | var markErrors = function(value, key) { |
| 577 | console.log("name='" + key + "'"); |
| 578 | var $inputElement = self.$el.find("[name='" + key + "']"); |
| 579 | var $inputContainer = $inputElement.parent(); |
| 580 | //$inputContainer.find(".help-inline").remove(); |
| 581 | var $errorEl = $("<span>", {class: "help-inline error", text: value}); |
| 582 | $inputContainer.append($errorEl).addClass("error"); |
| 583 | } |
| 584 | _.each(errors, markErrors); |
| 585 | }, |
| 586 | |
Scott Baker | 0a636cb | 2014-12-07 22:27:09 -0800 | [diff] [blame] | 587 | templateHelpers: function() { return { modelName: this.model.modelName, |
| 588 | collectionName: this.model.collectionName, |
| 589 | addFields: this.model.addFields, |
Scott Baker | e68d37b | 2014-12-09 16:59:08 -0800 | [diff] [blame] | 590 | listFields: this.model.listFields, |
Scott Baker | 0a636cb | 2014-12-07 22:27:09 -0800 | [diff] [blame] | 591 | detailFields: this.model.detailFields, |
| 592 | foreignFields: this.model.foreignFields, |
Scott Baker | e68d37b | 2014-12-09 16:59:08 -0800 | [diff] [blame] | 593 | detailLinkFields: this.model.detailLinkFields, |
Scott Baker | 0a636cb | 2014-12-07 22:27:09 -0800 | [diff] [blame] | 594 | inputType: this.model.inputType, |
Scott Baker | 07cdef0 | 2014-12-08 11:13:43 -0800 | [diff] [blame] | 595 | model: this.model, |
Scott Baker | 0a636cb | 2014-12-07 22:27:09 -0800 | [diff] [blame] | 596 | }}, |
| 597 | |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 598 | }); |
Scott Baker | c16b4c1 | 2014-11-03 23:54:24 -0800 | [diff] [blame] | 599 | |
| 600 | /* XOSItemView |
| 601 | This is for items that will be displayed as table rows. |
| 602 | extend with: |
| 603 | app - MarionetteApplication |
| 604 | template - template (See XOSHelper.html) |
Scott Baker | c16b4c1 | 2014-11-03 23:54:24 -0800 | [diff] [blame] | 605 | */ |
| 606 | |
| 607 | XOSItemView = Marionette.ItemView.extend({ |
| 608 | tagName: 'tr', |
| 609 | className: 'test-tablerow', |
| 610 | |
Scott Baker | c91396e | 2014-12-02 10:49:04 -0800 | [diff] [blame] | 611 | templateHelpers: function() { return { modelName: this.model.modelName, |
| 612 | collectionName: this.model.collectionName, |
Scott Baker | e68d37b | 2014-12-09 16:59:08 -0800 | [diff] [blame] | 613 | listFields: this.model.listFields, |
Scott Baker | 0a636cb | 2014-12-07 22:27:09 -0800 | [diff] [blame] | 614 | addFields: this.model.addFields, |
| 615 | detailFields: this.model.detailFields, |
| 616 | foreignFields: this.model.foreignFields, |
Scott Baker | e68d37b | 2014-12-09 16:59:08 -0800 | [diff] [blame] | 617 | detailLinkFields: this.model.detailLinkFields, |
Scott Baker | 0a636cb | 2014-12-07 22:27:09 -0800 | [diff] [blame] | 618 | inputType: this.model.inputType, |
Scott Baker | 07cdef0 | 2014-12-08 11:13:43 -0800 | [diff] [blame] | 619 | model: this.model, |
Scott Baker | c91396e | 2014-12-02 10:49:04 -0800 | [diff] [blame] | 620 | }}, |
Scott Baker | c16b4c1 | 2014-11-03 23:54:24 -0800 | [diff] [blame] | 621 | }); |
| 622 | |
| 623 | /* XOSListView: |
| 624 | extend with: |
| 625 | app - MarionetteApplication |
| 626 | childView - class of ItemView, probably an XOSItemView |
| 627 | template - template (see xosHelper.html) |
| 628 | collection - collection that holds these objects |
| 629 | title - title to display in template |
| 630 | */ |
| 631 | |
Scott Baker | f50222e | 2014-12-08 14:49:03 -0800 | [diff] [blame] | 632 | XOSListView = FilteredCompositeView.extend({ |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 633 | childViewContainer: 'tbody', |
Scott Baker | 07cdef0 | 2014-12-08 11:13:43 -0800 | [diff] [blame] | 634 | parentModel: null, |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 635 | |
| 636 | events: {"click button.btn-xos-add": "addClicked", |
| 637 | "click button.btn-xos-refresh": "refreshClicked", |
| 638 | }, |
| 639 | |
| 640 | _fetchStateChange: function() { |
| 641 | if (this.collection.fetching) { |
| 642 | $("#xos-list-title-spinner").show(); |
| 643 | } else { |
| 644 | $("#xos-list-title-spinner").hide(); |
| 645 | } |
| 646 | }, |
| 647 | |
Scott Baker | 7ce2365 | 2014-11-07 16:40:30 -0800 | [diff] [blame] | 648 | addClicked: function(e) { |
Scott Baker | 7ce2365 | 2014-11-07 16:40:30 -0800 | [diff] [blame] | 649 | e.preventDefault(); |
Scott Baker | a34d8c4 | 2014-11-11 18:02:35 -0800 | [diff] [blame] | 650 | this.app.Router.navigate("add" + firstCharUpper(this.collection.modelName), {trigger: true}); |
Scott Baker | 7ce2365 | 2014-11-07 16:40:30 -0800 | [diff] [blame] | 651 | }, |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 652 | |
| 653 | refreshClicked: function(e) { |
| 654 | e.preventDefault(); |
| 655 | this.collection.refresh(refreshRelated=true); |
| 656 | }, |
| 657 | |
| 658 | initialize: function() { |
| 659 | this.listenTo(this.collection, 'change', this._renderChildren) |
Scott Baker | f50222e | 2014-12-08 14:49:03 -0800 | [diff] [blame] | 660 | this.listenTo(this.collection, 'sort', function() { console.log("sort"); }) |
| 661 | this.listenTo(this.collection, 'add', function() { console.log("add"); }) |
Scott Baker | 13e6f0d | 2014-11-18 17:02:07 -0800 | [diff] [blame] | 662 | this.listenTo(this.collection, 'fetchStateChange', this._fetchStateChange); |
Scott Baker | c16b4c1 | 2014-11-03 23:54:24 -0800 | [diff] [blame] | 663 | |
| 664 | // Because many of the templates use idToName(), we need to |
| 665 | // listen to the collections that hold the names for the ids |
| 666 | // that we want to display. |
| 667 | for (i in this.collection.foreignCollections) { |
| 668 | foreignName = this.collection.foreignCollections[i]; |
| 669 | if (xos[foreignName] == undefined) { |
| 670 | console.log("Failed to find xos class " + foreignName); |
| 671 | } |
| 672 | this.listenTo(xos[foreignName], 'change', this._renderChildren); |
| 673 | this.listenTo(xos[foreignName], 'sort', this._renderChildren); |
| 674 | } |
| 675 | }, |
| 676 | |
Scott Baker | 07cdef0 | 2014-12-08 11:13:43 -0800 | [diff] [blame] | 677 | getAddChildHash: function() { |
| 678 | if (this.parentModel) { |
Scott Baker | cb90181 | 2014-12-09 17:27:52 -0800 | [diff] [blame] | 679 | parentFieldName = this.parentModel.relatedCollections[this.collection.collectionName]; |
| 680 | parentFieldName = parentFieldName || "unknown"; |
| 681 | |
| 682 | /*parentFieldName = "unknown"; |
| 683 | |
Scott Baker | 07cdef0 | 2014-12-08 11:13:43 -0800 | [diff] [blame] | 684 | for (fieldName in this.collection.foreignFields) { |
| 685 | cname = this.collection.foreignFields[fieldName]; |
| 686 | if (cname = this.collection.collectionName) { |
| 687 | parentFieldName = fieldName; |
| 688 | } |
Scott Baker | cb90181 | 2014-12-09 17:27:52 -0800 | [diff] [blame] | 689 | }*/ |
Scott Baker | 07cdef0 | 2014-12-08 11:13:43 -0800 | [diff] [blame] | 690 | return "#addChild" + firstCharUpper(this.collection.modelName) + "/" + this.parentModel.modelName + "/" + parentFieldName + "/" + this.parentModel.id; // modelName, fieldName, id |
| 691 | } else { |
| 692 | return null; |
| 693 | } |
| 694 | }, |
| 695 | |
Scott Baker | c16b4c1 | 2014-11-03 23:54:24 -0800 | [diff] [blame] | 696 | templateHelpers: function() { |
Scott Baker | 07cdef0 | 2014-12-08 11:13:43 -0800 | [diff] [blame] | 697 | return { title: this.title, |
Scott Baker | e68d37b | 2014-12-09 16:59:08 -0800 | [diff] [blame] | 698 | addChildHash: this.getAddChildHash(), |
| 699 | foreignFields: this.collection.foreignFields, |
| 700 | listFields: this.collection.listFields, |
| 701 | detailLinkFields: this.collection.detailLinkFields, }; |
Scott Baker | 5ac6abe | 2014-12-02 14:50:26 -0800 | [diff] [blame] | 702 | }, |
Scott Baker | c16b4c1 | 2014-11-03 23:54:24 -0800 | [diff] [blame] | 703 | }); |
| 704 | |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 705 | XOSDataTableView = Marionette.View.extend( { |
Scott Baker | eb2565f | 2014-12-12 00:18:11 -0800 | [diff] [blame^] | 706 | el: '<div style="overflow: hidden">' + |
| 707 | '<h3 class="xos-list-title title_placeholder"></h3>' + |
| 708 | '<div class="header_placeholder"></div>' + |
| 709 | '<table></table>' + |
| 710 | '<div class="footer_placeholder"></div>' + |
| 711 | '</div>', |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 712 | |
| 713 | filter: undefined, |
| 714 | |
Scott Baker | eb2565f | 2014-12-12 00:18:11 -0800 | [diff] [blame^] | 715 | events: {"click button.btn-xos-add": "addClicked", |
| 716 | "click button.btn-xos-refresh": "refreshClicked", |
| 717 | }, |
| 718 | |
| 719 | _fetchStateChange: function() { |
| 720 | if (this.collection.fetching) { |
| 721 | $("#xos-list-title-spinner").show(); |
| 722 | } else { |
| 723 | $("#xos-list-title-spinner").hide(); |
| 724 | } |
| 725 | }, |
| 726 | |
| 727 | addClicked: function(e) { |
| 728 | e.preventDefault(); |
| 729 | this.app.Router.navigate("add" + firstCharUpper(this.collection.modelName), {trigger: true}); |
| 730 | }, |
| 731 | |
| 732 | refreshClicked: function(e) { |
| 733 | e.preventDefault(); |
| 734 | this.collection.refresh(refreshRelated=true); |
| 735 | }, |
| 736 | |
| 737 | |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 738 | initialize: function() { |
Scott Baker | eb2565f | 2014-12-12 00:18:11 -0800 | [diff] [blame^] | 739 | $(this.el).find(".footer_placeholder").html( xosListFooterTemplate({addChildHash: this.getAddChildHash()}) ); |
| 740 | $(this.el).find(".header_placeholder").html( xosListHeaderTemplate() ); |
| 741 | |
| 742 | this.listenTo(this.collection, 'fetchStateChange', this._fetchStateChange); |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 743 | }, |
| 744 | |
| 745 | render: function() { |
| 746 | var view = this; |
| 747 | |
Scott Baker | a047336 | 2014-12-11 23:08:31 -0800 | [diff] [blame] | 748 | view.columnsByIndex = []; |
| 749 | view.columnsByFieldName = {}; |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 750 | _.each(this.collection.listFields, function(fieldName) { |
| 751 | mRender = undefined; |
Scott Baker | a047336 | 2014-12-11 23:08:31 -0800 | [diff] [blame] | 752 | mSearchText = undefined; |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 753 | if (fieldName in view.collection.foreignFields) { |
| 754 | var foreignCollection = view.collection.foreignFields[fieldName]; |
Scott Baker | a047336 | 2014-12-11 23:08:31 -0800 | [diff] [blame] | 755 | mSearchText = function(x) { return idToName(x, foreignCollection, "humanReadableName"); }; |
| 756 | } |
| 757 | if ($.inArray(fieldName, view.collection.detailLinkFields)>=0) { |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 758 | var collectionName = view.collection.collectionName; |
| 759 | mRender = function(x,y,z) { return '<a href="#' + collectionName + '/' + z.id + '">' + x + '</a>'; }; |
| 760 | } |
Scott Baker | a047336 | 2014-12-11 23:08:31 -0800 | [diff] [blame] | 761 | thisColumn = {sTitle: fieldNameToHumanReadable(fieldName), mData: fieldName, mRender: mRender, mSearchText: mSearchText}; |
| 762 | view.columnsByIndex.push( thisColumn ); |
| 763 | view.columnsByFieldName[fieldName] = thisColumn; |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 764 | }); |
| 765 | |
Scott Baker | eb2565f | 2014-12-12 00:18:11 -0800 | [diff] [blame^] | 766 | deleteColumn = {sTitle: "delete", mRender: function(x,y,z) { return xosDeleteButtonTemplate({modelName: view.collection.modelName, id: z.id}); }, mData: function() { return "delete"; }}; |
| 767 | view.columnsByIndex.push(deleteColumn); |
| 768 | view.columnsByFieldName["delete"] = deleteColumn; |
| 769 | |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 770 | oTable = $(this.el).find("table").dataTable( { |
| 771 | "bJQueryUI": true, |
| 772 | "bStateSave": true, |
| 773 | "bServerSide": true, |
Scott Baker | a047336 | 2014-12-11 23:08:31 -0800 | [diff] [blame] | 774 | "aoColumns": view.columnsByIndex, |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 775 | |
| 776 | fnServerData: function(sSource, aoData, fnCallback, settings) { |
| 777 | var compareColumns = function(sortCols, sortDirs, a, b) { |
Scott Baker | a047336 | 2014-12-11 23:08:31 -0800 | [diff] [blame] | 778 | a = a[sortCols[0]]; |
| 779 | b = b[sortCols[0]]; |
| 780 | result = (a==b) ? 0 : ((a<b) ? -1 : 1); |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 781 | if (sortDirs[0] == "desc") { |
| 782 | result = -result; |
| 783 | } |
| 784 | return result; |
| 785 | }; |
Scott Baker | a047336 | 2014-12-11 23:08:31 -0800 | [diff] [blame] | 786 | |
| 787 | var searchMatch = function(row, sSearch) { |
| 788 | for (fieldName in row) { |
| 789 | if (fieldName in view.columnsByFieldName) { |
| 790 | try { |
| 791 | value = row[fieldName].toString(); |
| 792 | } catch(e) { |
| 793 | continue; |
| 794 | } |
| 795 | if (value.indexOf(sSearch) >= 0) { |
| 796 | return true; |
| 797 | } |
| 798 | } |
| 799 | } |
| 800 | return false; |
| 801 | }; |
| 802 | |
Scott Baker | eb2565f | 2014-12-12 00:18:11 -0800 | [diff] [blame^] | 803 | //console.log(aoData); |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 804 |
|
| 805 | // function used to populate the DataTable with the current
|
| 806 | // content of the collection
|
| 807 | var populateTable = function()
|
| 808 | {
|
Scott Baker | eb2565f | 2014-12-12 00:18:11 -0800 | [diff] [blame^] | 809 | console.log("populatetable!");
|
| 810 |
|
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 811 | // clear out old row views
|
| 812 | rows = [];
|
| 813 |
|
Scott Baker | a047336 | 2014-12-11 23:08:31 -0800 | [diff] [blame] | 814 | sSearch = null;
|
| 815 | iDisplayStart = 0;
|
| 816 | iDisplayLength = 1000;
|
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 817 | sortDirs = [];
|
| 818 | sortCols = [];
|
| 819 | _.each(aoData, function(param) {
|
| 820 | if (param.name == "sSortDir_0") {
|
| 821 | sortDirs = [param.value];
|
| 822 | } else if (param.name == "iSortCol_0") {
|
Scott Baker | a047336 | 2014-12-11 23:08:31 -0800 | [diff] [blame] | 823 | sortCols = [view.columnsByIndex[param.value].mData];
|
| 824 | } else if (param.name == "iDisplayStart") {
|
| 825 | iDisplayStart = param.value;
|
| 826 | } else if (param.name == "iDisplayLength") {
|
| 827 | iDisplayLength = param.value;
|
| 828 | } else if (param.name == "sSearch") {
|
| 829 | sSearch = param.value;
|
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 830 | }
|
| 831 | });
|
| 832 |
|
| 833 | aaData = view.collection.toJSON();
|
| 834 |
|
Scott Baker | a047336 | 2014-12-11 23:08:31 -0800 | [diff] [blame] | 835 | // apply backbone filtering on the models
|
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 836 | if (view.filter) {
|
| 837 | aaData = aaData.filter( function(row) { model = {}; model.attributes = row; return view.filter(model); } );
|
| 838 | }
|
| 839 |
|
Scott Baker | a047336 | 2014-12-11 23:08:31 -0800 | [diff] [blame] | 840 | var totalSize = aaData.length;
|
| 841 |
|
| 842 | // turn the ForeignKey fields into human readable things
|
| 843 | for (rowIndex in aaData) {
|
| 844 | row = aaData[rowIndex];
|
| 845 | for (fieldName in row) {
|
| 846 | if (fieldName in view.columnsByFieldName) {
|
| 847 | mSearchText = view.columnsByFieldName[fieldName].mSearchText;
|
| 848 | if (mSearchText) {
|
| 849 | row[fieldName] = mSearchText(row[fieldName]);
|
| 850 | }
|
| 851 | }
|
| 852 | }
|
| 853 | }
|
| 854 |
|
| 855 | // apply datatables search
|
| 856 | if (sSearch) {
|
| 857 | aaData = aaData.filter( function(row) { return searchMatch(row, sSearch); });
|
| 858 | }
|
| 859 |
|
| 860 | var filteredSize = aaData.length;
|
| 861 |
|
| 862 | // apply datatables sort
|
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 863 | aaData.sort(function(a,b) { return compareColumns(sortCols, sortDirs, a, b); });
|
| 864 |
|
Scott Baker | a047336 | 2014-12-11 23:08:31 -0800 | [diff] [blame] | 865 | // slice it for pagination
|
| 866 | aaData = aaData.slice(iDisplayStart, iDisplayStart+iDisplayLength);
|
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 867 |
|
| 868 | return fnCallback({iTotalRecords: totalSize,
|
| 869 | iTotalDisplayRecords: filteredSize,
|
| 870 | aaData: aaData});
|
| 871 | };
|
| 872 |
|
| 873 | aoData.shift(); // ignore sEcho |
| 874 | populateTable(); |
Scott Baker | eb2565f | 2014-12-12 00:18:11 -0800 | [diff] [blame^] | 875 | |
| 876 | view.listenTo(view.collection, 'change', populateTable); |
| 877 | view.listenTo(view.collection, 'add', populateTable); |
| 878 | view.listenTo(view.collection, 'remove', populateTable); |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 879 | }, |
| 880 | } ); |
| 881 | |
| 882 | return this; |
| 883 | }, |
| 884 | |
Scott Baker | eb2565f | 2014-12-12 00:18:11 -0800 | [diff] [blame^] | 885 | getAddChildHash: function() { |
| 886 | if (this.parentModel) { |
| 887 | parentFieldName = this.parentModel.relatedCollections[this.collection.collectionName]; |
| 888 | parentFieldName = parentFieldName || "unknown"; |
| 889 | |
| 890 | /*parentFieldName = "unknown"; |
| 891 | |
| 892 | for (fieldName in this.collection.foreignFields) { |
| 893 | cname = this.collection.foreignFields[fieldName]; |
| 894 | if (cname = this.collection.collectionName) { |
| 895 | parentFieldName = fieldName; |
| 896 | } |
| 897 | }*/ |
| 898 | return "#addChild" + firstCharUpper(this.collection.modelName) + "/" + this.parentModel.modelName + "/" + parentFieldName + "/" + this.parentModel.id; // modelName, fieldName, id |
| 899 | } else { |
| 900 | return null; |
| 901 | } |
| 902 | }, |
| 903 | |
Scott Baker | 660b9e0 | 2014-12-11 02:27:04 -0800 | [diff] [blame] | 904 | }); |
| 905 | |
Scott Baker | fdaee92 | 2014-11-03 09:43:23 -0800 | [diff] [blame] | 906 | idToName = function(id, collectionName, fieldName) { |
Scott Baker | 3593520 | 2014-12-08 21:35:06 -0800 | [diff] [blame] | 907 | return xos.idToName(id, collectionName, fieldName); |
Scott Baker | fdaee92 | 2014-11-03 09:43:23 -0800 | [diff] [blame] | 908 | }; |
| 909 | |
| 910 | /* Constructs lists of <option> html blocks for items in a collection. |
| 911 | |
| 912 | selectedId = the id of an object that should be selected, if any |
| 913 | collectionName = name of collection |
| 914 | fieldName = name of field within models of collection that will be displayed |
| 915 | */ |
| 916 | |
| 917 | idToOptions = function(selectedId, collectionName, fieldName) { |
| 918 | result="" |
| 919 | for (index in xos[collectionName].models) { |
| 920 | linkedObject = xos[collectionName].models[index]; |
| 921 | linkedId = linkedObject["id"]; |
| 922 | linkedName = linkedObject.attributes[fieldName]; |
| 923 | if (linkedId == selectedId) { |
| 924 | selected = " selected"; |
| 925 | } else { |
| 926 | selected = ""; |
| 927 | } |
| 928 | result = result + '<option value="' + linkedId + '"' + selected + '>' + linkedName + '</option>'; |
| 929 | } |
| 930 | return result; |
| 931 | }; |
| 932 | |
| 933 | /* Constructs an html <select> and the <option>s to go with it. |
| 934 | |
| 935 | variable = variable name to return to form |
| 936 | selectedId = the id of an object that should be selected, if any |
| 937 | collectionName = name of collection |
| 938 | fieldName = name of field within models of collection that will be displayed |
| 939 | */ |
| 940 | |
Scott Baker | 07b4a25 | 2014-12-08 23:54:18 -0800 | [diff] [blame] | 941 | idToSelect = function(variable, selectedId, collectionName, fieldName, readOnly) { |
| 942 | if (readOnly) { |
| 943 | readOnly = " readonly"; |
| 944 | } else { |
| 945 | readOnly = ""; |
| 946 | } |
| 947 | result = '<select name="' + variable + '"' + readOnly + '>' + |
Scott Baker | fdaee92 | 2014-11-03 09:43:23 -0800 | [diff] [blame] | 948 | idToOptions(selectedId, collectionName, fieldName) + |
| 949 | '</select>'; |
Scott Baker | fdaee92 | 2014-11-03 09:43:23 -0800 | [diff] [blame] | 950 | return result; |
| 951 | } |
| 952 | |