tenant view, WIP
Scott Baker [Sun, 11 Jan 2015 21:44:30 +0000 (13:44 -0800)]
planetstack/core/xoslib/dashboards/xosTenant.html [new file with mode: 0644]
planetstack/core/xoslib/static/js/picker.js
planetstack/core/xoslib/static/js/xosTenant.js [new file with mode: 0644]
planetstack/core/xoslib/static/js/xoslib/xos-backbone.js
planetstack/core/xoslib/static/js/xoslib/xosHelper.js
planetstack/core/xoslib/templates/xosAdmin.html

diff --git a/planetstack/core/xoslib/dashboards/xosTenant.html b/planetstack/core/xoslib/dashboards/xosTenant.html
new file mode 100644 (file)
index 0000000..49236d4
--- /dev/null
@@ -0,0 +1,72 @@
+<script src="{{ STATIC_URL }}/js/vendor/underscore-min.js"></script>
+<script src="{{ STATIC_URL }}/js/vendor/backbone.js"></script>
+<script src="{{ STATIC_URL }}/js/vendor/backbone.syphon.js"></script>
+<script src="{{ STATIC_URL }}/js/vendor/backbone.wreqr.js"></script>
+<script src="{{ STATIC_URL }}/js/vendor/backbone.babysitter.js"></script>
+<script src="{{ STATIC_URL }}/js/vendor/backbone.marionette.js"></script>
+
+<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
+<link rel="stylesheet" type="text/css" href="{% static 'css/xosAdminDashboard.css' %}" media="all" >
+<link rel="stylesheet" type="text/css" href="{% static 'css/xosAdminSite.css' %}" media="all" >
+
+<script src="{{ STATIC_URL }}/js/xoslib/xos-util.js"></script>
+<script src="{{ STATIC_URL }}/js/xoslib/xos-defaults.js"></script>
+<script src="{{ STATIC_URL }}/js/xoslib/xos-validators.js"></script>
+<script src="{{ STATIC_URL }}/js/xoslib/xos-backbone.js"></script>
+<script src="{{ STATIC_URL }}/js/xoslib/xosHelper.js"></script>
+<script src="{{ STATIC_URL }}/js/xosTenant.js"></script>
+
+<script type="text/template" id="xos-tenant-buttons-template">
+  <div class="box save-box">
+    <button class="btn btn-high btn-tenant-create">Create New Slice</button>
+    <button class="btn btn-high btn-tenant-delete">Delete Slice</button>
+    <button class="btn btn-high btn-tenant-add-user">Add User</button>
+    <button class="btn btn-high btn-tenant-save">Save</button>
+  </div>
+</script>
+
+<script type="text/template" id="xos-log-template">
+  <tr id="<%= logMessageId %>" class="xos-log xos-<%= statusclass %>">
+     <td><%= what %><br>
+         <%= status %> <%= statusText %>
+     </td>
+  </tr>
+</script>
+
+<div id="xos-confirm-dialog" title="Confirmation Required">
+  Are you sure about this?\r
+</div>
+
+<div id="xos-error-dialog" title="Error Message">
+</div>\r
+\r
+<div id="contentPanel">\r
+<div id="contentTitle">
+</div>
+<div id="contentButtonPanel">
+
+<div id="rightButtonPanel"></div>
+
+<div class="box" id="logPanel">
+<table id="logTable">
+<tbody>
+</tbody>
+</table> <!-- end logTable -->
+</div> <!-- end logPanel -->
+</div> <!-- end contentButtonPanel -->
+
+<div id="contentInner">
+
+<div id="tenantSliceSelector">
+</div>
+<div id="tenantSummary">
+</div>
+<div id="tenantSiteList">
+</div>
+<div id="tenantButtons">
+</div>
+
+</div> <!-- end contentInner -->
+</div> <!-- end contentPanel -->
+
+{% include 'xosAdmin.html' %}
index 0302cf4..075bdc5 100644 (file)
@@ -48,3 +48,7 @@ function init_picker(selector, ordered) {
         });\r
     });\r
 };\r
+\r
+function init_spinner(selector, value) {\r
+     var spinner = $(selector).spinner( "value", value);\r
+};\r
diff --git a/planetstack/core/xoslib/static/js/xosTenant.js b/planetstack/core/xoslib/static/js/xosTenant.js
new file mode 100644 (file)
index 0000000..5095505
--- /dev/null
@@ -0,0 +1,218 @@
+XOSTenantSite = XOSModel.extend( {
+    listFields: ["name", "allocated"],
+    modelName: "tenantSite",
+    collectionName: "tenantSites"
+});
+
+XOSTenantSiteCollection = XOSCollection.extend( {
+    listFields: ["name", "allocated"],
+    modelName: "tenantSite",
+    collectionName: "tenantSites",
+
+    updateFromSlice: function(slice) {
+        var tenantSites = [];
+        var id = 0;
+        for (siteName in slice.attributes.site_allocation) {
+            allocated = slice.attributes.site_allocation[siteName];
+            tenantSites.push(new XOSTenantSite( { name: siteName, allocated: allocated, id: id} ));
+            id = id + 1;
+        }
+        for (index in xos.tenantview.models[0].attributes.blessed_site_names) {
+            siteName = xos.tenantview.models[0].attributes.blessed_site_names[index];
+            if (! (siteName in slice.attributes.site_allocation)) {
+                tenantSites.push(new XOSTenantSite( { name: siteName, allocated: 0, id: id} ));
+                id = id + 1;
+            }
+        }
+        this.set(tenantSites);
+    },
+});
+
+XOSTenantButtonView = Marionette.ItemView.extend({
+            template: "#xos-tenant-buttons-template",
+
+            events: {"click button.btn-tenant-create": "createClicked",
+                     "click button.btn-tenant-delete": "deleteClicked",
+                     "click button.btn-tenant-add-user": "addUserClicked",
+                     "click button.btn-tenant-save": "saveClicked",
+                     },
+
+            createClicked: function(e) {
+                     },
+
+            deleteClicked: function(e) {
+                     this.options.linkedView.deleteClicked.call(this.options.linkedView, e);
+                     },
+
+            addUserClicked: function(e) {
+                     },
+
+            saveClicked: function(e) {
+                     this.options.linkedView.submitContinueClicked.call(this.options.linkedView, e);
+                     },
+            });
+
+XOSTenantApp = new XOSApplication({
+    logTableId: "#logTable",
+    statusMsgId: "#statusMsg",
+    hideTabsByDefault: true
+});
+
+XOSTenantApp.addRegions({
+    tenantSliceSelector: "#tenantSliceSelector",
+    tenantSummary: "#tenantSummary",
+    tenantSiteList: "#tenantSiteList",
+    tenantButtons: "#tenantButtons",
+});
+
+XOSTenantApp.buildViews = function() {\r
+     XOSTenantApp.tenantSites = new XOSTenantSiteCollection();\r
+\r
+     tenantSummaryClass = XOSDetailView.extend({template: "#xos-detail-template",\r
+                                                app: XOSTenantApp,\r
+                                                detailFields: ["serviceClass", "image_preference", "network_ports", "mount_data_sets"]});\r
+\r
+     XOSTenantApp.tenantSummaryView = tenantSummaryClass;\r
+\r
+     tenantSiteItemClass = XOSItemView.extend({template: "#xos-listitem-template",\r
+                                               app: XOSTenantApp});\r
+\r
+     XOSTenantApp.tenantSiteItemView = tenantSiteItemClass;\r
+\r
+     tenantSiteListClass = XOSDataTableView.extend({template: "#xos-list-template",\r
+                                               app: XOSTenantApp,\r
+                                               childView: tenantSiteItemClass,\r
+                                               collection: XOSTenantApp.tenantSites,\r
+                                               title: "sites",\r
+                                               inputType: {allocated: "spinner"},\r
+                                               noDeleteColumn: true,\r
+                                               });\r
+\r
+     XOSTenantApp.tenantSiteListView = tenantSiteListClass;\r
+\r
+     XOSTenantApp.tenantSliceSelectorView = SliceSelectorView.extend( {\r
+         sliceChanged: function(id) {\r
+             //console.log("navigate to " + id);\r
+             XOSTenantApp.Router.navigate("slice/" + id, {trigger: true});\r
+         },\r
+     });\r
+\r
+     xos.sites.fetch();\r
+     xos.slicesPlus.fetch();\r
+     xos.tenantview.fetch();\r
+};\r
+\r
+make_choices = function(list_of_names, list_of_values) {\r
+    result = [];\r
+    if (!list_of_values) {\r
+        for (index in list_of_names) {\r
+            displayName = list_of_names[index];\r
+            result.push( [displayName, displayName] );\r
+        }\r
+    }\r
+    return result;\r
+};\r
+\r
+XOSTenantApp.viewSlice = function(model) {\r
+    if (!model && xos.slicesPlus.models.length > 0) {\r
+        model = xos.slicesPlus.models[0];\r
+    }\r
+\r
+    sliceSelector = new XOSTenantApp.tenantSliceSelectorView({collection: xos.slicesPlus,\r
+                                                              selectedID: model.id,\r
+                                                             } );\r
+    XOSTenantApp.sliceSelector = sliceSelector;\r
+    XOSTenantApp.tenantSliceSelector.show(sliceSelector);\r
+\r
+    tenantSummary = new XOSTenantApp.tenantSummaryView({model: model,\r
+                                                        choices: {mount_data_sets: make_choices(xos.tenantview.models[0].attributes.public_volume_names, null),\r
+                                                                  image_preference: make_choices(xos.tenantview.models[0].attributes.blessed_image_names, null)},\r
+                                                       });\r
+    XOSTenantApp.tenantSummary.show(tenantSummary);\r
+\r
+    tenantSites = new XOSTenantSiteCollection();\r
+    tenantSites.updateFromSlice(model);\r
+    XOSTenantApp.tenantSites = tenantSites;\r
+\r
+    tenantSiteList = new XOSTenantApp.tenantSiteListView({collection: tenantSites });\r
+    XOSTenantApp.tenantSiteList.show(tenantSiteList);\r
+    // on xos.slicePlus.sort, need to update xostenantapp.tenantSites\r
+\r
+    XOSTenantApp.tenantButtons.show( new XOSTenantButtonView( { app: XOSTenantApp,\r
+                                                                linkedView: tenantSummary } ) );\r
+};\r
+\r
+XOSTenantApp.initRouter = function() {\r
+    router = XOSRouter;\r
+    var api = {};\r
+    var routes = {};\r
+\r
+    nav_url = "slice/:id";\r
+    api_command = "viewSlice";\r
+    api[api_command] = function(id) { XOSTenantApp.viewSlice(xos.slicesPlus.get(id)); };\r
+    routes[nav_url] = api_command;\r
+\r
+    nav_url = "increase/:collectionName/:id/:fieldName";\r
+    api_command = "increase";\r
+    api[api_command] = function(collectionName, id, fieldName) {\r
+                           XOSTenantApp.Router.showPreviousURL();\r
+                           model = XOSTenantApp[collectionName].get(id);\r
+                           model.set(fieldName, model.get(fieldName) + 1);\r
+                       };\r
+    routes[nav_url] = api_command;\r
+\r
+    nav_url = "decrease/:collectionName/:id/:fieldName";\r
+    api_command = "decrease";\r
+    api[api_command] = function(collectionName, id, fieldName) {\r
+                           XOSTenantApp.Router.showPreviousURL();\r
+                           model = XOSTenantApp[collectionName].get(id);\r
+                           model.set(fieldName, Math.max(0, model.get(fieldName) - 1));\r
+                       };\r
+    routes[nav_url] = api_command;\r
+\r
+    nav_url = "*path";\r
+    api_command = "defaultRoute";\r
+    api[api_command] = function() { XOSTenantApp.viewSlice(undefined); };\r
+    routes[nav_url] = api_command;\r
+\r
+    XOSTenantApp.Router = new router({ appRoutes: routes, controller: api });\r
+};\r
+\r
+XOSTenantApp.startNavigation = function() {\r
+    Backbone.history.start();\r
+    XOSTenantApp.navigationStarted = true;\r
+}\r
+\r
+XOSTenantApp.collectionLoadChange = function() {\r
+    stats = xos.getCollectionStatus();\r
+\r
+    if (!XOSTenantApp.navigationStarted) {\r
+        if (stats["isLoaded"] + stats["failedLoad"] >= stats["startedLoad"]) {\r
+            XOSTenantApp.startNavigation();\r
+\r
+            //if (xos.slicesPlus.models.length > 0) {\r
+            //    XOSTenantApp.Router.navigate("slice/" + xos.slicesPlus.models[0].id, {trigger:true});\r
+            //}\r
+        } else {\r
+            $("#tenantSummary").html("<h3>Loading...</h3><div id='xos-startup-progress'></div>");\r
+            $("#xos-startup-progress").progressbar({value: stats["completedLoad"], max: stats["startedLoad"]});\r
+        }\r
+    }\r
+};\r
+\r
+XOSTenantApp.on("start", function() {\r
+     XOSTenantApp.buildViews();
+
+     XOSTenantApp.initRouter();
+
+     // fire it once to initially show the progress bar
+     XOSTenantApp.collectionLoadChange();
+
+     // fire it each time the collection load status is updated
+     Backbone.on("xoslib:collectionLoadChange", XOSTenantApp.collectionLoadChange);
+});
+
+$(document).ready(function(){
+    XOSTenantApp.start();
+});
+
index 47ea66a..762a2b5 100644 (file)
@@ -34,8 +34,16 @@ if (! window.XOSLIB_LOADED ) {
     USERDEPLOYMENT_API = "/plstackapi/userdeployments/";
 
     SLICEPLUS_API = "/xoslib/slicesplus/";
+    TENANTVIEW_API = "/xoslib/tenantview/"
 
     XOSModel = Backbone.Model.extend({
+        relatedCollections: [],
+        foreignCollections: [],
+        foreignFields: {},
+        m2mFields: {},
+        readonlyFields: [],
+        detailLinkFields: [],
+
         /* from backbone-tastypie.js */
         //idAttribute: 'resource_uri',
 
@@ -152,6 +160,10 @@ if (! window.XOSLIB_LOADED ) {
 \r
         relatedCollections: [],\r
         foreignCollections: [],\r
+        foreignFields: {},\r
+        m2mFields: {},
+        readonlyFields: [],
+        detailLinkFields: [],\r
 \r
         sorted: function() {\r
             //console.log("sorted " + this.modelName);\r
@@ -333,6 +345,22 @@ if (! window.XOSLIB_LOADED ) {
             },
     });
 
+    function get_defaults(modelName) {
+        if ((typeof xosdefaults !== "undefined") && xosdefaults[modelName]) {
+            return xosdefaults[modelName];
+        }
+        return undefined;
+    }
+
+    function extend_defaults(modelName, stuff) {
+        defaults = get_defaults(modelName);
+        if (defaults) {
+            return $.extend({}, defaults, stuff);
+        } else {
+            return stuff;
+        }
+    }
+
     function define_model(lib, attrs) {
         modelName = attrs.modelName;
         modelClassName = modelName;
@@ -358,7 +386,7 @@ if (! window.XOSLIB_LOADED ) {
 
         for (key in attrs) {
             value = attrs[key];
-            if ($.inArray(key, ["urlRoot", "modelName", "collectionName", "listFields", "addFields", "detailFields", "detailLinkFields", "foreignFields", "inputType", "relatedCollections", "foreignCollections"])>=0) {
+            if ($.inArray(key, ["urlRoot", "modelName", "collectionName", "listFields", "addFields", "detailFields", "detailLinkFields", "foreignFields", "inputType", "relatedCollections", "foreignCollections", "defaults"])>=0) {
                 modelAttrs[key] = value;
                 collectionAttrs[key] = value;
             }
@@ -367,9 +395,15 @@ if (! window.XOSLIB_LOADED ) {
             }
         }
 
-        if ((typeof xosdefaults !== "undefined") && xosdefaults[modelName]) {
-            modelAttrs["defaults"] = xosdefaults[modelName];
+        if (!modelAttrs.defaults) {
+            modelAttrs.defaults = get_defaults(modelName);
         }
+        console.log(modelName);
+        console.log(modelAttrs);
+
+//        if ((typeof xosdefaults !== "undefined") && xosdefaults[modelName]) {
+//            modelAttrs["defaults"] = xosdefaults[modelName];
+//        }
 
         if ((typeof xosvalidators !== "undefined") && xosvalidators[modelName]) {
             modelAttrs["validators"] = xosvalidators[modelName];
@@ -629,9 +663,36 @@ if (! window.XOSLIB_LOADED ) {
         // enhanced REST
         // XXX this really needs to somehow be combined with Slice, to avoid duplication
         define_model(this, {urlRoot: SLICEPLUS_API,
-                            relatedCollections: {'slivers': "slice"},
-                            modelName: "slicePlus",
-                            collectionName: "slicesPlus"});
+                           relatedCollections: {"slivers": "slice", "slicePrivileges": "slice", "networks": "owner"},
+                           foreignCollections: ["services", "sites"],
+                           foreignFields: {"service": "services", "site": "sites"},
+                           listFields: ["backend_status", "id", "name", "enabled", "description", "slice_url", "site", "max_slivers", "service"],
+                           detailFields: ["backend_status", "name", "site", "enabled", "description", "slice_url", "max_slivers"],
+                           inputType: {"enabled": "checkbox"},
+                           modelName: "slicePlus",
+                           collectionName: "slicesPlus",
+                           defaults: extend_defaults("slice", {"network_ports": "", "site_allocation": []}),
+                           xosValidate: function(attrs, options) {
+                               errors = XOSModel.prototype.xosValidate(this, attrs, options);
+                               // validate that slice.name starts with site.login_base
+                               site = attrs.site || this.site;
+                               if ((site!=undefined) && (attrs.name!=undefined)) {
+                                   site = xos.sites.get(site);
+                                   if (attrs.name.indexOf(site.attributes.login_base+"_") != 0) {
+                                        errors = errors || {};
+                                        errors["name"] = "must start with " + site.attributes.login_base + "_";
+                                   }
+                               }
+                               return errors;
+                             },
+                           });
+
+        define_model(this, {urlRoot: TENANTVIEW_API,
+                            modelName: "tenantview",
+                            collectionName: "tenantview",
+                            listFields: [],
+                            detailFields: [],
+                            });
 
         this.listObjects = function() { return this.allCollectionNames; };
 
index 6c0c2f3..0cadf79 100644 (file)
@@ -4,6 +4,41 @@ HTMLView = Marionette.ItemView.extend({
   },
 });
 
+SliceSelectorOption = Marionette.ItemView.extend({
+    template: "#xos-sliceselector-option",
+    tagName: "option",
+    attributes: function() {
+        console.log("XXX");
+        console.log(this.options.selectedID);
+        console.log(this.model.get("id"));
+        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 +60,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 +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} );
             }
@@ -103,6 +139,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 {
@@ -392,7 +430,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) {
@@ -501,13 +539,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);
@@ -609,12 +647,13 @@ 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,
                                                     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 || {},
                                          }},
 });
 
@@ -829,6 +868,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);
@@ -840,6 +880,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} ); };
             }
             if ($.inArray(fieldName, view.collection.detailLinkFields)>=0) {
                 var collectionName = view.collection.collectionName;
@@ -850,9 +892,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,
@@ -893,7 +937,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
@@ -1044,3 +1088,25 @@ idToSelect = function(variable, selectedId, collectionName, fieldName, readOnly,
     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;
+}
index 4862cea..ac90e94 100644 (file)
   <% args = arguments; %>\r
   <% _.each(detailFields, function(fieldName) { %>\r
      <tr><td><%= fieldNameToHumanReadable(fieldName) %>:</td>\r
-        <% readOnly = $.inArray(fieldName, model.readOnlyFields)>=0 ? " readonly" : "";  console.log(fieldName + " " + readOnly); console.log(model.readOnlyFields); %>\r
-        <% if (fieldName in foreignFields) { %>\r
+        <% readOnly = $.inArray(fieldName, model.readOnlyFields)>=0 ? " readonly" : "";  %>\r
+        <% if (fieldName in choices) { %>\r
+            <td><%= choicesToSelect(fieldName, model.attributes[fieldName], choices[fieldName]) %></td>\r
+        <% } else if (fieldName in foreignFields) { %>\r
             <td><%= idToSelect(fieldName, model.attributes[fieldName], foreignFields[fieldName], "humanReadableName", readOnly) %></td>\r
+        <% } else if (inputType[fieldName] == "spinner") { %>\r
+            <!-- note: I never finished working on this spinner stuff! -->\r
+            <td><%= xosSpinnerTemplate({id: "picker_" + fieldName, fieldName: fieldName, value: model.attributes[fieldName]}) %></td>\r
         <% } else if (inputType[fieldName] == "checkbox") { %>\r
             <td><input type="checkbox" name="<%= fieldName %>" <% if (model.attributes[fieldName]) print("checked"); %><%= readOnly %>></td>\r
         <% } else if (inputType[fieldName] == "picker") { %>\r
   </div>
 </script>
 
+<script type="text/template" id="xos-datatable-spinner-template">
+    <!-- arguments: value, id, collectionName, fieldName -->
+    <%= value %> <a href='#increase/<%= collectionName %>/<%= id %>/<%= fieldName %>'>more</a> <a href='#decrease/<%= collectionName %>/<%= id %>/<%= fieldName %>'>less</a>
+</script>
+
+<script type="text/template" id="xos-spinner-template">
+    <!-- arguments: fieldName, id, value -->
+    <input name="<%= fieldName %>" class="xos-spinner" id="<%= id %>">
+    <% detailView.viewInitializers.push( function() { init_spinner("#" + id, value); } ); %>
+</script>
 
 <script type="text/template" id="xos-picker-template">
-    <!-- arguments: unpickedItems, pickedItems -->
+    <!-- arguments: unpickedItems, pickedItems, fieldName, id -->
     <div id="<%= id %>">
     <div class="picker_row">
     <div class="picker_column">\r
     <% detailView.viewInitializers.push( function() { init_picker("#" + id); } ); %>
 </script>
 
+<script type="text/template" id="xos-sliceselector-option">
+   <%= name %>
+</script>
+
+<script type="text/template" id="xos-sliceselector-select">
+    <select>
+    </select>
+</script>
+
 <script>
 xosInlineDetailButtonsTemplate = _.template($("#xos-inline-detail-buttons-template").html());
 xosListHeaderTemplate = _.template($("#xos-list-header-template").html());
@@ -268,5 +292,7 @@ xosDetailLinkTemplate = _.template($("#xos-detail-link-template").html());
 xosBackendStatusIconTemplate = _.template($("#xos-backend-status-icon-template").html());
 xosBackendStatusTextTemplate = _.template($("#xos-backend-status-text-template").html());
 xosPickerTemplate = _.template($("#xos-picker-template").html());
+xosSpinnerTemplate = _.template($("#xos-spinner-template").html());
+xosDataTableSpinnerTemplate = _.template($("#xos-datatable-spinner-template").html());
 </script>