X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=planetstack%2Fcore%2Fxoslib%2Fstatic%2Fjs%2Fxoslib%2FxosHelper.js;h=76254f6d16605456145fc802addc9c608325b69d;hb=753a13edfd39603fbdc59747c4d7c2b59a2d06b9;hp=ece241959f4bd2f698c43385e53f3746c520f82f;hpb=ea014bd9dff3f14102e505d7b8f4af3938720495;p=plstackapi.git diff --git a/planetstack/core/xoslib/static/js/xoslib/xosHelper.js b/planetstack/core/xoslib/static/js/xoslib/xosHelper.js index ece2419..76254f6 100644 --- a/planetstack/core/xoslib/static/js/xoslib/xosHelper.js +++ b/planetstack/core/xoslib/static/js/xoslib/xosHelper.js @@ -4,6 +4,41 @@ HTMLView = Marionette.ItemView.extend({ }, }); +SliceSelectorOption = Marionette.ItemView.extend({ + template: "#xos-sliceselector-option", + tagName: "option", + attributes: function() { + if (this.options.selectedID == this.model.get("id")) { + return { value: this.model.get("id"), selected: 1 }; + } else { + return { value: this.model.get("id") }; + } + }, +}); + +SliceSelectorView = Marionette.CompositeView.extend({ + template: "#xos-sliceselector-select", + childViewContainer: "select", + childView: SliceSelectorOption, + caption: "Slice", + + events: {"change select": "onSliceChanged"}, + + childViewOptions: function() { + return { selectedID: this.options.selectedID || this.selectedID || null }; + }, + + onSliceChanged: function() { + this.sliceChanged(this.$el.find("select").val()); + }, + + sliceChanged: function(id) { + console.log("sliceChanged " + id); + }, + + templateHelpers: function() { return {caption: this.options.caption || this.caption }; }, +}); + FilteredCompositeView = Marionette.CompositeView.extend( { showCollection: function() { var ChildView; @@ -25,6 +60,7 @@ XOSRouter = Marionette.AppRouter.extend({ onRoute: function(x,y,z) { this.routeStack.push(Backbone.history.fragment); + this.routeStack = this.routeStack.slice(-32); // limit the size of routeStack to something reasonable }, prevPage: function() { @@ -33,8 +69,8 @@ XOSRouter = Marionette.AppRouter.extend({ showPreviousURL: function() { prevPage = this.prevPage(); - console.log("showPreviousURL"); - console.log(this.routeStack); + //console.log("showPreviousURL"); + //console.log(this.routeStack); if (prevPage) { this.navigate("#"+prevPage, {trigger: false, replace: true} ); } @@ -47,8 +83,25 @@ XOSRouter = Marionette.AppRouter.extend({ Marionette.AppRouter.prototype.navigate.call(this, href, options); }, }); - - + +// XXX - We import backbone multiple times (BAD!) since the import happens +// inside of the view's html. The second time it's imported (developer +// view), it wipes out Backbone.Syphon. So, save it as Backbone_Syphon for +// now. +Backbone_Syphon = Backbone.Syphon +Backbone_Syphon.InputReaders.register('select', function(el) { + // Modify syphon so that if a select has "syphonall" in the class, then + // the value of every option will be returned, regardless of whether of + // not it is selected. + if (el.hasClass("syphonall")) { + result = []; + _.each(el.find("option"), function(option) { + result.push($(option).val()); + }); + return result; + } + return el.val(); +}); XOSApplication = Marionette.Application.extend({ detailBoxId: "#detailBox", @@ -91,6 +144,8 @@ XOSApplication = Marionette.Application.extend({ parsed_error=undefined; width=640; // django stacktraces like wide width } + console.log(responseText); + console.log(parsed_error); if (parsed_error) { $("#xos-error-dialog").html(templateFromId("#xos-error-response")(parsed_error)); } else { @@ -353,6 +408,8 @@ XOSApplication = Marionette.Application.extend({ that.destroyModel(model); if (afterDelete=="list") { that.navigate("list", modelName); + } else if (afterDelete) { + afterDelete(); } }); }, @@ -380,7 +437,7 @@ XOSButtonView = Marionette.ItemView.extend({ }, submitDeleteClicked: function(e) { - this.options.linkedView.submitDeleteClicked.call(this.options.linkedView, e); + this.options.linkedView.deleteClicked.call(this.options.linkedView, e); }, addClicked: function(e) { @@ -404,6 +461,8 @@ XOSListButtonView = XOSButtonView.extend({ template: "#xos-listbuttons-template" XOSDetailView = Marionette.ItemView.extend({ tagName: "div", + viewInitializers: [], + events: {"click button.btn-xos-save-continue": "submitContinueClicked", "click button.btn-xos-save-leave": "submitLeaveClicked", "click button.btn-xos-save-another": "submitAddAnotherClicked", @@ -416,15 +475,30 @@ XOSDetailView = Marionette.ItemView.extend({ */ initialize: function() { - this.on("saveSuccess", this.onAfterSave); + this.on("saveSuccess", this.onSaveSuccess); this.synchronous = false; }, + onShow: function() { + _.each(this.viewInitializers, function(initializer) { + initializer(); + }); + }, + + saveSuccess: function(e) { + // always called after a save succeeds + }, + afterSave: function(e) { + // if this.synchronous, then called after the save succeeds + // if !this.synchronous, then called after save is initiated }, - onAfterSave: function(e) { - this.afterSave(e); + onSaveSuccess: function(e) { + this.saveSuccess(e); + if (this.synchronous) { + this.afterSave(e); + } }, inputChanged: function(e) { @@ -441,6 +515,9 @@ XOSDetailView = Marionette.ItemView.extend({ submitLeaveClicked: function(e) { console.log("saveLeave"); e.preventDefault(); + if (this.options.noSubmitButton || this.noSubmitButton) { + return; + } var that=this; this.afterSave = function() { that.app.navigate("list", that.model.modelName); @@ -462,10 +539,12 @@ XOSDetailView = Marionette.ItemView.extend({ save: function() { this.app.hideError(); - var data = Backbone.Syphon.serialize(this); + var data = Backbone_Syphon.serialize(this); var that = this; var isNew = !this.model.id; + console.log(data); + this.$el.find(".help-inline").remove(); /* although model.validate() is called automatically by @@ -479,19 +558,17 @@ XOSDetailView = Marionette.ItemView.extend({ } if (isNew) { - this.model.attributes.humanReadableName = "new " + model.modelName; + this.model.attributes.humanReadableName = "new " + this.model.modelName; this.model.addToCollection = this.collection; } else { this.model.addToCollection = undefined; } - var infoMsgId = this.app.showInformational( {what: "save " + model.modelName + " " + model.attributes.humanReadableName, status: "", statusText: "in progress..."} ); + var infoMsgId = this.app.showInformational( {what: "save " + this.model.modelName + " " + this.model.attributes.humanReadableName, status: "", statusText: "in progress..."} ); this.model.save(data, {error: function(model, result, xhr) { that.app.saveError(model,result,xhr,infoMsgId);}, success: function(model, result, xhr) { that.app.saveSuccess(model,result,xhr,infoMsgId); - if (that.synchronous) { - that.trigger("saveSuccess"); - } + that.trigger("saveSuccess"); }}); this.dirty = false; @@ -574,7 +651,6 @@ XOSDetailView = Marionette.ItemView.extend({ onFormDataInvalid: function(errors) { var self=this; var markErrors = function(value, key) { - console.log("name='" + key + "'"); var $inputElement = self.$el.find("[name='" + key + "']"); var $inputContainer = $inputElement.parent(); //$inputContainer.find(".help-inline").remove(); @@ -588,13 +664,76 @@ XOSDetailView = Marionette.ItemView.extend({ collectionName: this.model.collectionName, addFields: this.model.addFields, listFields: this.model.listFields, - detailFields: this.model.detailFields, + detailFields: this.options.detailFields || this.detailFields || this.model.detailFields, + fieldDisplayNames: this.options.fieldDisplayNames || this.fieldDisplayNames || this.model.fieldDisplayNames || {}, foreignFields: this.model.foreignFields, detailLinkFields: this.model.detailLinkFields, inputType: this.model.inputType, model: this.model, + detailView: this, + choices: this.options.choices || this.choices || this.model.choices || {}, + helpText: this.options.helpText || this.helpText || this.model.helpText || {}, }}, +}); +XOSDetailView_sliver = XOSDetailView.extend( { + events: $.extend(XOSDetailView.events, + {"change #field_deploymentNetwork": "onDeploymentNetworkChange"} + ), + + onShow: function() { + // Note that this causes the selects to be updated a second time. The + // first time was when the template was originally invoked, and the + // selects will all have the full unfiltered set of candidates. Then + // onShow will fire, and we'll update them with the filtered values. + this.onDeploymentNetworkChange(); + }, + + onDeploymentNetworkChange: function(e) { + var deploymentID = this.$el.find("#field_deploymentNetwork").val(); + + console.log("onDeploymentNetworkChange"); + console.log(deploymentID); + + filterFunc = function(model) { return (model.attributes.deployment==deploymentID); } + newSelect = idToSelect("node", + this.model.attributes.node, + this.model.foreignFields["node"], + "humanReadableName", + false, + filterFunc); + this.$el.find("#field_node").html(newSelect); + + filterFunc = function(model) { for (index in model.attributes.deployments) { + item=model.attributes.deployments[index]; + if (item.toString()==deploymentID.toString()) return true; + }; + return false; + } + newSelect = idToSelect("flavor", + this.model.attributes.flavor, + this.model.foreignFields["flavor"], + "humanReadableName", + false, + filterFunc); + this.$el.find("#field_flavor").html(newSelect); + + filterFunc = function(model) { for (index in xos.imageDeployments.models) { + imageDeployment = xos.imageDeployments.models[index]; + if ((imageDeployment.attributes.deployment == deploymentID) && (imageDeployment.attributes.image == model.id)) { + return true; + } + } + return false; + }; + newSelect = idToSelect("image", + this.model.attributes.image, + this.model.foreignFields["image"], + "humanReadableName", + false, + filterFunc); + this.$el.find("#field_image").html(newSelect); + }, }); /* XOSItemView @@ -744,13 +883,15 @@ XOSDataTableView = Marionette.View.extend( { render: function() { var view = this; + var fieldDisplayNames = view.options.fieldDisplayNames || view.fieldDisplayNames || {}; view.columnsByIndex = []; view.columnsByFieldName = {}; _.each(this.collection.listFields, function(fieldName) { + inputType = view.options.inputType || view.inputType || {}; mRender = undefined; mSearchText = undefined; - sTitle = fieldNameToHumanReadable(fieldName); + sTitle = fieldName in fieldDisplayNames ? fieldDisplayNames[fieldName] : fieldNameToHumanReadable(fieldName); bSortable = true; if (fieldName=="backend_status") { mRender = function(x,y,z) { return xosBackendStatusIconTemplate(z); }; @@ -759,6 +900,8 @@ XOSDataTableView = Marionette.View.extend( { } else if (fieldName in view.collection.foreignFields) { var foreignCollection = view.collection.foreignFields[fieldName]; mSearchText = function(x) { return idToName(x, foreignCollection, "humanReadableName"); }; + } else if (inputType[fieldName] == "spinner") { + mRender = function(x,y,z) { return xosDataTableSpinnerTemplate( {value: x, collectionName: view.collection.collectionName, fieldName: fieldName, id: z.id, app: view.app} ); }; } if ($.inArray(fieldName, view.collection.detailLinkFields)>=0) { var collectionName = view.collection.collectionName; @@ -769,14 +912,18 @@ XOSDataTableView = Marionette.View.extend( { view.columnsByFieldName[fieldName] = thisColumn; }); - deleteColumn = {sTitle: "", bSortable: false, mRender: function(x,y,z) { return xosDeleteButtonTemplate({modelName: view.collection.modelName, id: z.id}); }, mData: function() { return "delete"; }}; - view.columnsByIndex.push(deleteColumn); - view.columnsByFieldName["delete"] = deleteColumn; + if (!view.noDeleteColumn) { + deleteColumn = {sTitle: "", bSortable: false, mRender: function(x,y,z) { return xosDeleteButtonTemplate({modelName: view.collection.modelName, id: z.id}); }, mData: function() { return "delete"; }}; + view.columnsByIndex.push(deleteColumn); + view.columnsByFieldName["delete"] = deleteColumn; + }; oTable = $(this.el).find("table").dataTable( { "bJQueryUI": true, "bStateSave": true, "bServerSide": true, + "bFilter": ! (view.options.disableFilter || view.disableFilter), + "bPaginate": ! (view.options.disablePaginate || view.disablePaginate), "aoColumns": view.columnsByIndex, fnServerData: function(sSource, aoData, fnCallback, settings) { @@ -812,7 +959,7 @@ XOSDataTableView = Marionette.View.extend( { // content of the collection var populateTable = function() { - console.log("populatetable!"); + //console.log("populatetable!"); // clear out old row views rows = []; @@ -869,7 +1016,9 @@ XOSDataTableView = Marionette.View.extend( { aaData.sort(function(a,b) { return compareColumns(sortCols, sortDirs, a, b); }); // slice it for pagination - aaData = aaData.slice(iDisplayStart, iDisplayStart+iDisplayLength); + if (iDisplayLength >= 0) { + aaData = aaData.slice(iDisplayStart, iDisplayStart+iDisplayLength); + } return fnCallback({iTotalRecords: totalSize, iTotalDisplayRecords: filteredSize, @@ -913,6 +1062,10 @@ idToName = function(id, collectionName, fieldName) { return xos.idToName(id, collectionName, fieldName); }; +makeIdToName = function(collectionName, fieldName) { + return function(id) { return idToName(id, collectionName, fieldName); } +}; + /* Constructs lists of '; } return result; @@ -944,15 +1100,37 @@ idToOptions = function(selectedId, collectionName, fieldName) { fieldName = name of field within models of collection that will be displayed */ -idToSelect = function(variable, selectedId, collectionName, fieldName, readOnly) { +idToSelect = function(variable, selectedId, collectionName, fieldName, readOnly, filterFunc) { if (readOnly) { readOnly = " readonly"; } else { readOnly = ""; } - result = '' + + idToOptions(selectedId, collectionName, fieldName, filterFunc) + ''; return result; } +choicesToOptions = function(selectedValue, choices) { + result=""; + for (index in choices) { + choice = choices[index]; + displayName = choice[0]; + value = choice[1]; + if (value == selectedValue) { + selected = " selected"; + } else { + selected = ""; + } + result = result + ''; + } + return result; +} + +choicesToSelect = function(variable, selectedValue, choices) { + result = ''; + return result; +}