edit users button in tenant view
[plstackapi.git] / planetstack / core / xoslib / static / js / xoslib / xosHelper.js
index ece2419..e8c1b10 100644 (file)
@@ -4,6 +4,38 @@ 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,
+
+    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);
+    },
+});
+
 FilteredCompositeView = Marionette.CompositeView.extend( {
     showCollection: function() {
       var ChildView;
@@ -25,6 +57,7 @@ XOSRouter = Marionette.AppRouter.extend({
 \r
         onRoute: function(x,y,z) {\r
              this.routeStack.push(Backbone.history.fragment);\r
+             this.routeStack = this.routeStack.slice(-32);   // limit the size of routeStack to something reasonable\r
         },\r
 \r
         prevPage: function() {\r
@@ -33,8 +66,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 +80,25 @@ XOSRouter = Marionette.AppRouter.extend({
             Marionette.AppRouter.prototype.navigate.call(this, href, options);
         },
     });\r
-
-
+\r
+// XXX - We import backbone multiple times (BAD!) since the import happens\r
+//   inside of the view's html. The second time it's imported (developer\r
+//   view), it wipes out Backbone.Syphon. So, save it as Backbone_Syphon for\r
+//   now.\r
+Backbone_Syphon = Backbone.Syphon\r
+Backbone_Syphon.InputReaders.register('select', function(el) {\r
+    // 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 +141,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 +405,8 @@ XOSApplication = Marionette.Application.extend({
             that.destroyModel(model);
             if (afterDelete=="list") {
                 that.navigate("list", modelName);
+            } else if (afterDelete) {
+                afterDelete();
             }
         });
     },
@@ -380,7 +434,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 +458,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",
@@ -420,6 +476,12 @@ XOSDetailView = Marionette.ItemView.extend({
                 this.synchronous = false;
             },
 
+            onShow: function() {
+                _.each(this.viewInitializers, function(initializer) {
+                    initializer();
+                });
+            },
+
             afterSave: function(e) {
             },
 
@@ -441,6 +503,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 +527,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,13 +546,13 @@ 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);
@@ -574,7 +641,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 +654,75 @@ 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 || {},
                                          }},
+});
 
+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
@@ -748,6 +876,7 @@ XOSDataTableView = Marionette.View.extend( {
         view.columnsByIndex = [];
         view.columnsByFieldName = {};
         _.each(this.collection.listFields, function(fieldName) {
+            inputType = view.options.inputType || view.inputType || {};
             mRender = undefined;
             mSearchText = undefined;
             sTitle = fieldNameToHumanReadable(fieldName);
@@ -759,6 +888,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,9 +900,11 @@ 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,
@@ -812,7 +945,7 @@ XOSDataTableView = Marionette.View.extend( {
                 // content of the collection\r
                 var populateTable = function()\r
                 {\r
-                  console.log("populatetable!");\r
+                  //console.log("populatetable!");\r
 \r
                   // clear out old row views\r
                   rows = [];\r
@@ -913,6 +1046,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 <option> html blocks for items in a collection.
 
    selectedId = the id of an object that should be selected, if any
@@ -920,7 +1057,7 @@ idToName = function(id, collectionName, fieldName) {
    fieldName = name of field within models of collection that will be displayed
 */
 
-idToOptions = function(selectedId, collectionName, fieldName) {
+idToOptions = function(selectedId, collectionName, fieldName, filterFunc) {
     result=""
     for (index in xos[collectionName].models) {
         linkedObject = xos[collectionName].models[index];
@@ -931,6 +1068,9 @@ idToOptions = function(selectedId, collectionName, fieldName) {
         } else {
             selected = "";
         }
+        if ((filterFunc) && (!filterFunc(linkedObject))) {
+            continue;
+        }
         result = result + '<option value="' + linkedId + '"' + selected + '>' + linkedName + '</option>';
     }
     return result;
@@ -944,15 +1084,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 = '<select name="' + variable + '"' + readOnly + '>' +
-             idToOptions(selectedId, collectionName, fieldName) +
+    result = '<select name="' + variable + '" id="field_' + variable + '"' + readOnly + '>' +
+             idToOptions(selectedId, collectionName, fieldName, filterFunc) +
              '</select>';
     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 + '<option value="' + value + '"' + selected + '>' + displayName + '</option>';
+    }
+    return result;
+}
+
+choicesToSelect = function(variable, selectedValue, choices) {
+    result = '<select name="' + variable + '" id="field_' + variable + '">' +
+             choicesToOptions(selectedValue, choices) +
+             '</select>';
+    return result;
+}