You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by sj...@apache.org on 2014/08/19 12:30:39 UTC

[1/9] git commit: Catalog page uses Backbone.history

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master 21a037f75 -> 226f55fa5


Catalog page uses Backbone.history


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/98ff36ff
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/98ff36ff
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/98ff36ff

Branch: refs/heads/master
Commit: 98ff36ff2e0f8f063e72194d9171fd9a88d3d393
Parents: cf1775b
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Fri Aug 8 16:33:13 2014 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Aug 13 17:23:16 2014 +0100

----------------------------------------------------------------------
 usage/jsgui/src/main/webapp/assets/js/router.js | 20 +++++----
 .../src/main/webapp/assets/js/view/catalog.js   | 43 ++++++++++++++++----
 2 files changed, 46 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/98ff36ff/usage/jsgui/src/main/webapp/assets/js/router.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/router.js b/usage/jsgui/src/main/webapp/assets/js/router.js
index f72baf8..029fad7 100644
--- a/usage/jsgui/src/main/webapp/assets/js/router.js
+++ b/usage/jsgui/src/main/webapp/assets/js/router.js
@@ -19,17 +19,17 @@
 define([
     "brooklyn", "underscore", "jquery", "backbone",
     "model/application", "model/app-tree", "model/location", "model/ha",
-    "view/home", "view/application-explorer", "view/catalog", "view/apidoc", "view/script-groovy", 
+    "view/home", "view/application-explorer", "view/catalog", "view/apidoc", "view/script-groovy",
     "text!tpl/help/page.html","text!tpl/labs/page.html", "text!tpl/home/server-not-ha-master.html"
 ], function (Brooklyn, _, $, Backbone,
         Application, AppTree, Location, ha,
-        HomeView, ExplorerView, CatalogView, ApidocView, ScriptGroovyView, 
+        HomeView, ExplorerView, CatalogView, ApidocView, ScriptGroovyView,
         HelpHtml, LabsHtml, ServerNotMasterHtml) {
 
     // TODO this initialising - customising the View prototype - should be moved,
     // and perhaps expanded to include other methods from viewutils
     // see discussion at https://github.com/brooklyncentral/brooklyn/pull/939
-    
+
     // add close method to all views for clean-up
     // (NB we have to update the prototype _here_ before any views are instantiated;
     //  see "close" called below in "showView") 
@@ -96,6 +96,7 @@ define([
             'v1/applications/*trail':'applicationsPage',
             'v1/applications':'applicationsPage',
             'v1/locations':'catalogPage',
+            'v1/catalog/:kind/:id':'catalogPage',
             'v1/catalog':'catalogPage',
             'v1/apidoc':'apidocPage',
             'v1/script/groovy':'scriptGroovyPage',
@@ -150,13 +151,14 @@ define([
                 if (trail !== undefined) appExplorer.show(trail)
             }})
         },
-        catalogPage:function () {
-            var that = this
+        catalogPage: function (catalogItemKind, id) {
             var catalogResource = new CatalogView({
-                locations:that.locations,
-                appRouter:that
-            })
-            that.showView("#application-content", catalogResource)
+                locations: this.locations,
+                appRouter: this,
+                kind: catalogItemKind,
+                id: id
+            });
+            this.showView("#application-content", catalogResource);
         },
         apidocPage:function () {
             var apidocResource = new ApidocView({})

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/98ff36ff/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
index 0e89c58..db927b9 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
@@ -212,6 +212,7 @@ define([
                 activeDetailsView = this.name;
                 this.activeCid = cid;
                 var model = this.collection.get(cid);
+                Backbone.history.navigate("v1/catalog/" + this.name + "/" + model.id);
                 this.options.onItemSelected(model, $event);
             }
         },
@@ -253,21 +254,24 @@ define([
                     name: "applications",
                     onItemSelected: _.partial(this.showCatalogItem, DetailsEntityHtml),
                     model: Entity.Model,
-                    autoOpen: true
+                    autoOpen: !this.options.kind || this.options.kind == "applications"
                 }),
                 "entities": new AccordionItemView({
                     name: "entities",
                     onItemSelected: _.partial(this.showCatalogItem, DetailsEntityHtml),
-                    model: Entity.Model
+                    model: Entity.Model,
+                    autoOpen: this.options.kind == "entities"
                 }),
                 "policies": new AccordionItemView({
                     onItemSelected: _.partial(this.showCatalogItem, DetailsGenericHtml),
-                    name: "policies"
+                    name: "policies",
+                    autoOpen: this.options.kind == "policies"
                 }),
                 "locations": new AccordionItemView({
                     name: "locations",
                     onItemSelected: _.partial(this.showCatalogItem, LocationDetailsHtml),
                     collection: this.options.locations,
+                    autoOpen: this.options.kind == "locations",
                     entryTemplateArgs: function (location, index) {
                         return {
                             type: location.getPrettyName(),
@@ -284,14 +288,16 @@ define([
 
         render: function() {
             this.$el.html(_.template(CatalogPageHtml, {}));
-
-            // Show empty details view to start
-            this.setDetailsView(new CatalogItemDetailsView().render());
-
             var parent = this.$(".catalog-accordion-parent");
             _.each(this.accordion, function(child) {
                 parent.append(child.render().$el);
             });
+            if (this.options.kind && this.options.id) {
+                this.loadAccordionItem(this.options.kind, this.options.id)
+            } else {
+                // Show empty details view to start
+                this.setDetailsView(new CatalogItemDetailsView().render());
+            }
             return this
         },
 
@@ -302,11 +308,32 @@ define([
 
         createNewThing: function(event) {
 
+        loadAccordionItem: function (kind, id) {
+            if (!this.accordion[kind]) {
+                console.error("No accordion for: " + kind);
+            } else {
+                var accordion = this.accordion[kind];
+                accordion.collection.fetch()
+                    .then(function() {
+                        var model = accordion.collection.get(id);
+                        if (!model) {
+                            console.log("Accordion for " + kind + " has no element with id " + id);
+                        } else {
+                            activeDetailsView = kind;
+                            accordion.activeCid = model.cid;
+                            accordion.options.onItemSelected(model);
+                        }
+                    });
+            }
         },
 
         showCatalogItem: function(template, model, $target) {
             this.$(".accordion-nav-row").removeClass("active");
-            $target.addClass("active");
+            if ($target) {
+                $target.addClass("active");
+            } else {
+                this.$("[data-cid=" + model.cid + "]").addClass("active");
+            }
             var newView = new CatalogItemDetailsView({
                 model: model,
                 template: template


[7/9] git commit: Adds inputValue and bindModelFromForm functions to brooklyn-util.js

Posted by sj...@apache.org.
Adds inputValue and bindModelFromForm functions to brooklyn-util.js


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/7724f121
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/7724f121
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/7724f121

Branch: refs/heads/master
Commit: 7724f1216615850a54d3d8109347e3e4a8f16392
Parents: 97d6d89
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Fri Aug 8 18:56:04 2014 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Aug 13 17:23:17 2014 +0100

----------------------------------------------------------------------
 .../webapp/assets/js/libs/brooklyn-utils.js     | 34 ++++++++++++-
 .../javascript/specs/brooklyn-utils-spec.js     | 52 +++++++++++++++++++-
 2 files changed, 82 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7724f121/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-utils.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-utils.js b/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-utils.js
index 4207823..1fc8905 100644
--- a/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-utils.js
+++ b/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-utils.js
@@ -1,6 +1,6 @@
 define([
-    'underscore'
-], function (_) {
+    'jquery', 'underscore'
+], function ($, _) {
 
     var Util = {};
 
@@ -79,6 +79,36 @@ define([
         return a.pathname;
     };
 
+    /**
+     * Extracts the value of the given input. Returns true/false for for checkboxes
+     * rather than "on" or "off".
+     */
+    Util.inputValue = function($input) {
+        if ($input.attr("type") === "checkbox") {
+            return $input.is(":checked");
+        } else {
+            return $input.val();
+        }
+    };
+
+    /**
+     * Updates or initialises the given model with the values of named elements under
+     * the given element. Force-updates the model by setting silent: true.
+     */
+    Util.bindModelFromForm = function(modelOrConstructor, $el) {
+        var model = _.isFunction(modelOrConstructor) ? new modelOrConstructor() : modelOrConstructor;
+        var inputs = {};
+
+        // Look up all named elements
+        $("[name]", $el).each(function(idx, inp) {
+            var input = $(inp);
+            var name = input.attr("name");
+            inputs[name] = Util.inputValue(input);
+        });
+        model.set(inputs, { silent: true });
+        return model;
+    };
+
     return Util;
 
 });

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7724f121/usage/jsgui/src/test/javascript/specs/brooklyn-utils-spec.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/test/javascript/specs/brooklyn-utils-spec.js b/usage/jsgui/src/test/javascript/specs/brooklyn-utils-spec.js
index ba9d32c..2c4012e 100644
--- a/usage/jsgui/src/test/javascript/specs/brooklyn-utils-spec.js
+++ b/usage/jsgui/src/test/javascript/specs/brooklyn-utils-spec.js
@@ -17,8 +17,8 @@
  * under the License.
  */
 define([
-    'brooklyn-utils'
-], function (Util) {
+    'brooklyn-utils', "backbone"
+], function (Util, Backbone) {
 
     describe('Rounding numbers', function () {
 
@@ -83,4 +83,52 @@ define([
             expect(Util.pathOf("/a/b/c/d#e")).toBe("/a/b/c/d");
         })
     });
+
+    describe("inputValue", function () {
+        it("should return inputs as strings", function () {
+            expect(Util.inputValue($('<input type="text" value="bob"/>'))).toBe("bob");
+            expect(Util.inputValue($('<textarea rows="10" cols="5">content</textarea>'))).toBe("content");
+        });
+
+        it("should return true/false for checkboxes", function () {
+            var input = $('<input type="checkbox" checked/>');
+            expect(Util.inputValue(input)).toBe(true);
+            input = $('<input type="checkbox" />');
+            expect(Util.inputValue(input)).toBe(false);
+        });
+    });
+
+    describe("bindModelFromForm", function () {
+        // pretend to be a Backbone model without bringing in Backbone as a dependency
+        var TestModel = Backbone.Model.extend({
+            urlRoot: function () {
+                return "/foo/bar/";
+            }
+
+        });
+        var form = $("<form>" +
+            "<input name='id' type='input' value='text'/>" +
+            "<input name='bool' type='checkbox' checked/>" +
+            "</form>");
+
+        it("should create a new model if given a constructor", function () {
+            var model = Util.bindModelFromForm(TestModel, form);
+            expect(model instanceof TestModel).toBe(true);
+            expect(model.url()).toBe("/foo/bar/text");
+            var inputs = model.attributes;
+            expect(_.keys(inputs).length).toBe(2);
+            expect(inputs.id).toBe("text");
+            expect(inputs.bool).toBe(true);
+        });
+
+        it("should update an existing model", function () {
+            var model = new TestModel({initialAttribute: "xyz"});
+            Util.bindModelFromForm(model, form);
+            var inputs = model.attributes;
+            expect(_.keys(inputs).length).toBe(3);
+            expect(inputs.id).toBe("text");
+            expect(inputs.bool).toBe(true);
+            expect(inputs.initialAttribute).toBe("xyz");
+        });
+    });
 });


[5/9] git commit: Callers to router.js can defer actions to post-ha-status-loaded

Posted by sj...@apache.org.
Callers to router.js can defer actions to post-ha-status-loaded


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/97d6d891
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/97d6d891
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/97d6d891

Branch: refs/heads/master
Commit: 97d6d891dc7a23691e0969ca1c7dd2b200b1aafe
Parents: 98ff36f
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Fri Aug 8 16:34:29 2014 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Aug 13 17:23:17 2014 +0100

----------------------------------------------------------------------
 usage/jsgui/src/main/webapp/assets/js/router.js | 41 +++++++++++++-------
 1 file changed, 27 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/97d6d891/usage/jsgui/src/main/webapp/assets/js/router.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/router.js b/usage/jsgui/src/main/webapp/assets/js/router.js
index 029fad7..9ef8053 100644
--- a/usage/jsgui/src/main/webapp/assets/js/router.js
+++ b/usage/jsgui/src/main/webapp/assets/js/router.js
@@ -70,24 +70,37 @@ define([
         this._periodicFunctions[uid] = setInterval(periodic, interval)
     }
 
+    /**
+     * @returns {jquery.Deferred}
+     *      A promise that resolves when the high availability status has been
+     *      loaded. Actions to be taken on the view after it has loaded should
+     *      be registered with calls to .done()
+     */
     // Not just defined as a function on Router because the delay if the HA status
     // hasn't loaded requires a reference to the function, which we lose if we use
     // 'this.showView'.
-    var showViewImpl = function (router, selector, view) {
+    function showViewImpl(router, selector, view) {
         // Don't do anything until the HA status has loaded.
-        if (!ha.loaded) {
-            _.delay(showViewImpl, 100, router, selector, view);
-        } else {
-            // close the previous view - does binding clean-up and avoids memory leaks
-            if (router.currentView) {
-                router.currentView.close();
+        var promise = $.Deferred()
+            .done(function () {
+                // close the previous view - does binding clean-up and avoids memory leaks
+                if (router.currentView) {
+                    router.currentView.close();
+                }
+                // render the view inside the selector element
+                $(selector).html(view.render().el);
+                router.currentView = view;
+                return view
+            });
+        (function isComplete() {
+            if (ha.loaded) {
+                promise.resolve();
+            } else {
+                _.defer(isComplete, 100);
             }
-            // render the view inside the selector element
-            $(selector).html(view.render().el);
-            router.currentView = view;
-            return view
-        }
-    };
+        })();
+        return promise;
+    }
 
     var Router = Backbone.Router.extend({
         routes:{
@@ -106,7 +119,7 @@ define([
         },
 
         showView: function(selector, view) {
-            showViewImpl(this, selector, view);
+            return showViewImpl(this, selector, view);
         },
 
         defaultRoute: function() {


[4/9] git commit: jsgui: Add new entities to catalogue

Posted by sj...@apache.org.
jsgui: Add new entities to catalogue


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/ee59971b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/ee59971b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/ee59971b

Branch: refs/heads/master
Commit: ee59971bc50903e7fdc690f8e3f77f44d6451773
Parents: 002f70f
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Tue Aug 12 15:13:14 2014 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Aug 13 17:23:17 2014 +0100

----------------------------------------------------------------------
 usage/jsgui/src/main/webapp/assets/css/base.css |   7 +
 usage/jsgui/src/main/webapp/assets/js/router.js |   2 +-
 .../src/main/webapp/assets/js/view/catalog.js   | 133 +++++++++++++++++--
 .../webapp/assets/js/view/effector-invoke.js    |   2 +-
 .../assets/tpl/catalog/add-catalog-entry.html   |  33 +++++
 .../webapp/assets/tpl/catalog/add-entity.html   |  30 +++++
 .../main/webapp/assets/tpl/catalog/page.html    |  48 +------
 .../javascript/specs/brooklyn-utils-spec.js     |   1 -
 8 files changed, 195 insertions(+), 61 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee59971b/usage/jsgui/src/main/webapp/assets/css/base.css
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/css/base.css b/usage/jsgui/src/main/webapp/assets/css/base.css
index c4d74df..8c50bf8 100644
--- a/usage/jsgui/src/main/webapp/assets/css/base.css
+++ b/usage/jsgui/src/main/webapp/assets/css/base.css
@@ -1134,6 +1134,13 @@ tr.app-add-wizard-config-entry {
     padding-bottom: 180px;
     text-align: center;
 }
+.catalog-save-error {
+    background-color: #f2dede;
+    /* margin matches bootstrap input margin-bottom. */
+    margin: 9px 0 0 0;
+    padding: 7px;
+    border-radius: 3px;
+}
 
 .float-right {
     float: right;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee59971b/usage/jsgui/src/main/webapp/assets/js/router.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/router.js b/usage/jsgui/src/main/webapp/assets/js/router.js
index 9ef8053..c52c220 100644
--- a/usage/jsgui/src/main/webapp/assets/js/router.js
+++ b/usage/jsgui/src/main/webapp/assets/js/router.js
@@ -109,7 +109,7 @@ define([
             'v1/applications/*trail':'applicationsPage',
             'v1/applications':'applicationsPage',
             'v1/locations':'catalogPage',
-            'v1/catalog/:kind/:id':'catalogPage',
+            'v1/catalog/:kind(/:id)':'catalogPage',
             'v1/catalog':'catalogPage',
             'v1/apidoc':'apidocPage',
             'v1/script/groovy':'scriptGroovyPage',

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee59971b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
index db927b9..4c38b5a 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
@@ -17,18 +17,21 @@
  * under the License.
 */
 define([
-    "underscore", "jquery", "backbone", "formatJson",
+    "underscore", "jquery", "backbone", "formatJson", "brooklyn",
     "model/location", "model/entity",
-    "view/catalog-add-location-modal",
     "text!tpl/catalog/page.html",
     "text!tpl/catalog/details-entity.html",
     "text!tpl/catalog/details-generic.html",
-    "text!tpl/catalog/nav-entry.html",
     "text!tpl/catalog/details-location.html",
+    "text!tpl/catalog/add-catalog-entry.html",
+    "text!tpl/catalog/add-entity.html",
+    "text!tpl/catalog/nav-entry.html",
 
     "bootstrap", "jquery-form"
-], function(_, $, Backbone, FormatJSON, Location, Entity, AddLocationModalView,
-        CatalogPageHtml, DetailsEntityHtml, DetailsGenericHtml, EntryHtml, LocationDetailsHtml) {
+], function(_, $, Backbone, FormatJSON, Brooklyn,
+        Location, Entity,
+        CatalogPageHtml, DetailsEntityHtml, DetailsGenericHtml, LocationDetailsHtml,
+        AddCatalogEntryHtml, AddEntityHtml, EntryHtml) {
 
     // Holds the currently active details type, e.g. applications, policies. Bit of a workaround
     // to share the active view with all instances of AccordionItemView, so clicking the 'reload
@@ -116,6 +119,89 @@ define([
         }
     });
 
+    var AddCatalogEntryView = Backbone.View.extend({
+        template: _.template(AddCatalogEntryHtml),
+        events: {
+            "click .show-context": "showContext"
+        },
+        initialize: function() {
+            _.bindAll(this);
+        },
+        render: function (initialView) {
+            this.$el.html(this.template());
+            if (initialView) {
+                this.$("[data-context='"+initialView+"']").addClass("active");
+                this.showFormForType(initialView)
+            }
+            return this;
+        },
+        beforeClose: function () {
+            if (this.contextView) {
+                this.contextView.close();
+            }
+        },
+        showContext: function(event) {
+            var $event = $(event.currentTarget);
+            var context = $event.data("context");
+            if (this.context !== context) {
+                if (this.contextView) {
+                    this.contextView.close();
+                }
+                this.showFormForType(context)
+            }
+        },
+        showFormForType: function (type) {
+            this.context = type;
+            if (type == "entity") {
+                this.contextView = newEntityForm(this.options.parent);
+            } else if (type !== undefined) {
+                console.log("unknown catalog type " + type);
+                this.showFormForType("entity");
+                return;
+            }
+            Backbone.history.navigate("/v1/catalog/new/" + type);
+            this.$("#catalog-add-form").html(this.contextView.$el);
+        }
+    });
+
+    function newEntityForm(parent) {
+        return new Brooklyn.view.Form({
+            template: AddEntityHtml,
+            onSubmit: function (model) {
+                console.log("Submit entity", model.get("yaml"));
+                var submitButton = this.$(".catalog-submit-button");
+                // "loading" is an indicator to Bootstrap, not a string to display
+                submitButton.button("loading");
+                var self = this;
+                var options = {
+                    url: "/v1/catalog/",
+                    data: model.get("yaml"),
+                    processData: false,
+                    type: "post"
+                };
+                $.ajax(options)
+                    .done(function (data, status, xhr) {
+                        // Can extract location of new item with:
+                        //model.url = Brooklyn.util.pathOf(xhr.getResponseHeader("Location"));
+                        parent.loadAccordionItem("entities", data.id);
+                    })
+                    .fail(function (xhr, status, error) {
+                        var message;
+                        try {
+                            message = JSON.parse(xhr.responseText).message;
+                        } catch (e) {
+                            message = "Error adding catalog item: " + error;
+                        }
+                        submitButton.button("reset");
+                        self.$(".catalog-save-error")
+                            .removeClass("hide")
+                            .find(".catalog-error-message")
+                            .html(message);
+                    });
+            }
+        });
+    }
+
     var Catalog = Backbone.Collection.extend({
         initialize: function(models, options) {
             this.name = options["name"];
@@ -225,6 +311,14 @@ define([
             } else {
                 body.slideUp('fast')
             }
+        },
+
+        show: function() {
+            var body = this.$(".accordion-body");
+            var hidden = this.hidden = body.css("display") == "none";
+            if (hidden) {
+                body.removeClass("hide").slideDown('fast');
+            }
         }
     });
 
@@ -236,11 +330,7 @@ define([
 
         events: {
             'click .refresh':'refresh',
-            'click #add-new-thing':'createNewThing',
-            'click #add-new-entity':'addNewCatalogResource',
-            'click #new-entity-submit':'newEntitySubmit',
-            'click #add-new-location':'addLocation',
-            'click .delete-location':'deleteLocation'
+            'click #add-new-thing': 'createNewThing'
         },
 
         initialize: function() {
@@ -292,7 +382,9 @@ define([
             _.each(this.accordion, function(child) {
                 parent.append(child.render().$el);
             });
-            if (this.options.kind && this.options.id) {
+            if (this.options.kind === "new") {
+                this.createNewThing(this.options.id);
+            } else if (this.options.kind && this.options.id) {
                 this.loadAccordionItem(this.options.kind, this.options.id)
             } else {
                 // Show empty details view to start
@@ -306,7 +398,22 @@ define([
             _.invoke(this.accordion, 'refresh');
         },
 
-        createNewThing: function(event) {
+        createNewThing: function (type) {
+            // Discard if it's the jquery event object.
+            if (!_.isString(type)) {
+                type = undefined;
+            }
+            var viewName = "createNewThing";
+            if (!type) {
+                Backbone.history.navigate("/v1/catalog/new");
+            }
+            activeDetailsView = viewName;
+            this.$(".accordion-nav-row").removeClass("active");
+            var newView = new AddCatalogEntryView({
+                parent: this
+            }).render(type);
+            this.setDetailsView(newView);
+        },
 
         loadAccordionItem: function (kind, id) {
             if (!this.accordion[kind]) {
@@ -322,6 +429,7 @@ define([
                             activeDetailsView = kind;
                             accordion.activeCid = model.cid;
                             accordion.options.onItemSelected(model);
+                            accordion.show();
                         }
                     });
             }
@@ -353,7 +461,6 @@ define([
             }
             this.detailsView = view;
         }
-        
     });
     
     return CatalogResourceView

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee59971b/usage/jsgui/src/main/webapp/assets/js/view/effector-invoke.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/effector-invoke.js b/usage/jsgui/src/main/webapp/assets/js/view/effector-invoke.js
index af25019..bf93cc7 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/effector-invoke.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/effector-invoke.js
@@ -63,7 +63,7 @@ define([
             this.$el.html(this.template({
                 name:this.model.get("name"),
                 entityName:this.options.entity.get("name"),
-                description:this.model.get("description")?this.model.get("description"):"",
+                description:this.model.get("description")?this.model.get("description"):""
             }))
             // do we have parameters to render?
             if (params.length !== 0) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee59971b/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-catalog-entry.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-catalog-entry.html b/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-catalog-entry.html
new file mode 100644
index 0000000..16fb9ee
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-catalog-entry.html
@@ -0,0 +1,33 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<div class="catalog-details">
+
+    <h2>Add a new...</h2>
+    <br/>
+
+    <div data-toggle="buttons-radio">
+        <button class="btn btn-large show-context" data-context="entity">Entity</button>
+    </div>
+
+    <hr/>
+
+    <div id="catalog-add-form">
+        <div class="context">Select the type you wish to add</div>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee59971b/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-entity.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-entity.html b/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-entity.html
new file mode 100644
index 0000000..9c64e90
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-entity.html
@@ -0,0 +1,30 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<form>
+    <label for="new-blueprint">Enter blueprint:</label>
+    <textarea id='new-blueprint' name='yaml' rows='5' cols='15'></textarea>
+
+    <button class='catalog-submit-button btn' data-loading-text='Saving...'>Submit</button>
+    <p class="catalog-save-error hide">
+        <span class="alert-error">
+            <strong>Error:</strong><br/>
+            <span class="catalog-error-message"></span>
+        </span>
+    </p>
+</form>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee59971b/usage/jsgui/src/main/webapp/assets/tpl/catalog/page.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/catalog/page.html b/usage/jsgui/src/main/webapp/assets/tpl/catalog/page.html
index 2c6a156..dc1b102 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/catalog/page.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/catalog/page.html
@@ -16,16 +16,15 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 -->
-
-<!-- Catalog page template -->
 <div id="catalog-resource">
     <div class="row row-fluid">
         <div class="span4" id="accord">
             <div class="navbar_top">
                 <h3>Catalog</h3>
-
                 <div class="apps-tree-toolbar">
-                    <i class="icon-br-refresh refresh handy"/>
+                    <i id="add-new-thing" class="icon-br-plus-sign handy"></i>
+                    &nbsp;
+                    <i class="icon-br-refresh refresh handy"></i>
                 </div>
             </div>
             <div class="navbar_main_wrapper">
@@ -36,44 +35,3 @@ under the License.
         <div class="span8" id="details"></div>
     </div>
 </div>
-
-<div class="modal hide fade" id="new-entity-modal">
-    <form class="form-vertical" id="new-entity-form">
-
-        <div class="modal-header">
-            <button type="button" class="close" data-dismiss="modal">x</button>
-            <h3>Upload Catalog Resource</h3>
-        </div>
-
-        <div class="modal-body">
-
-            <fieldset>
-                <div class="row">
-                    <div class="span5">
-
-                        <label for="groovy-code" class="control-label">Select the source code file for the template/entity/policy you wish to upload.</label>
-                        <br/>
-                        <div class="control-group">
-                            <div class="controls">
-                                <input id="groovy-code" name="groovyCode" type="file" class="input-large">
-                            </div>
-                        </div>
-                        <br/>
-                    </div>
-                </div>
-            </fieldset>
-            <div id="info-box">
-                <p><span class="label label-info">Note:</span> This window will close automatically when file is
-                    uploaded</p>
-            </div>
-        </div>
-
-        <div class="modal-footer form-actions">
-            <button class="btn" data-dismiss="modal" type="button">Close</button>
-            <button id="new-entity-submit" class="btn btn-primary" type="button">Save</button>
-        </div>
-
-    </form>
-</div>
-
-<div class="modal hide fade" id="new-location-modal"></div>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee59971b/usage/jsgui/src/test/javascript/specs/brooklyn-utils-spec.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/test/javascript/specs/brooklyn-utils-spec.js b/usage/jsgui/src/test/javascript/specs/brooklyn-utils-spec.js
index 2c4012e..ab9566d 100644
--- a/usage/jsgui/src/test/javascript/specs/brooklyn-utils-spec.js
+++ b/usage/jsgui/src/test/javascript/specs/brooklyn-utils-spec.js
@@ -104,7 +104,6 @@ define([
             urlRoot: function () {
                 return "/foo/bar/";
             }
-
         });
         var form = $("<form>" +
             "<input name='id' type='input' value='text'/>" +


[3/9] git commit: Fix display of generic details and make entity details messages consistent

Posted by sj...@apache.org.
Fix display of generic details and make entity details messages consistent


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/68ae2354
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/68ae2354
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/68ae2354

Branch: refs/heads/master
Commit: 68ae235499d22e2ac7cfe9cdbe724a6b83314cda
Parents: 1a69146
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Thu Aug 7 18:00:43 2014 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Aug 13 17:23:16 2014 +0100

----------------------------------------------------------------------
 .../assets/tpl/catalog/details-entity.html      | 10 +++++---
 .../assets/tpl/catalog/details-generic.html     | 27 +++++++++++++-------
 2 files changed, 25 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/68ae2354/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-entity.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-entity.html b/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-entity.html
index dac12f5..eaab99b 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-entity.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-entity.html
@@ -52,7 +52,7 @@ under the License.
                     <% } else if (!model.get("config")) { %>
                         <p>Loading...</p>
                     <% } else if (_.isEmpty(model.get("config"))) { %>
-                        <p>None available</p>
+                        <p>No configuration</p>
                     <% } else { %>
                         <% var skip = [
                             'name',
@@ -90,7 +90,9 @@ under the License.
                 <div class="accordion-inner">
                     <% if (model.error) { %>
                     <p><i class="icon-exclamation-sign"></i> Could not load sensors</p>
-                    <% } else if (!model.get("sensors") || _.isEmpty(model.get("sensors"))) { %>
+                    <% } else if (!model.get("sensors")) { %>
+                        <p>Loading...</p>
+                    <% } else if (_.isEmpty(model.get("sensors"))) { %>
                         <p>No sensors</p>
                     <% } else { %>
                         <table class="table table-striped table-condensed nonDatatables">
@@ -125,7 +127,9 @@ under the License.
                 <div class="accordion-inner">
                 <% if (model.error) { %>
                     <p><i class="icon-exclamation-sign"></i> Could not load effectors</p>
-                <% } else if (!model.get("effectors") || _.isEmpty(model.get("effectors"))) { %>
+                <% } else if (!model.get("effectors")) { %>
+                    <p>Loading...</p>
+                <% } else if (_.isEmpty(model.get("effectors"))) { %>
                     <p>No effectors</p>
                 <% } else { %>
                     <% _.each(model.get("effectors"), function(object, index) { %>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/68ae2354/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-generic.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-generic.html b/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-generic.html
index 4277ac6..f57500e 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-generic.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-generic.html
@@ -17,16 +17,25 @@ specific language governing permissions and limitations
 under the License.
 -->
 
-<div class="catalog-details-generic">
+<div class="catalog-details">
 
-<% if ((typeof title !== 'undefined') && (title.length > 0)) { %>
-    <h3><%= title %></h3>
-<% } %>
+    <% if (model.get("name") !== undefined) { %>
+        <h2><%= model.get("name") %></h2>
+    <% } else if (model.get("type") !== undefined) { %>
+        <h2><%= model.get("type") %></h2>
+    <% } %>
 
-<% if (typeof json === 'undefined') { %>
-    <i>Loading...</i>
-<% } else { %>
-    <textarea readonly="readonly" rows="25" style="width:100%;"><%= json %></textarea>
-<% } %>
+    <dl>
+        <% _.each(model.attributes, function(value, key) { %>
+            <% if (value) { %>
+                <dt><%= key %></dt>
+                <% if (_.isObject(value)) { %>
+                    <dd>Not shown: is a complex object</dd>
+                <% } else { %>
+                    <dd><%= value %></dd>
+                <% } %>
+            <% } %>
+        <% }) %>
+    </dl>
 
 </div>


[9/9] git commit: This closes #116

Posted by sj...@apache.org.
This closes #116


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/226f55fa
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/226f55fa
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/226f55fa

Branch: refs/heads/master
Commit: 226f55fa5678f3533a187cb646c8f57be107cfcb
Parents: 21a037f ee59971
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Tue Aug 19 11:03:41 2014 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Tue Aug 19 11:03:41 2014 +0100

----------------------------------------------------------------------
 usage/jsgui/src/main/webapp/assets/css/base.css |   7 +
 usage/jsgui/src/main/webapp/assets/js/config.js |   1 +
 .../webapp/assets/js/libs/brooklyn-utils.js     |  34 +-
 .../main/webapp/assets/js/libs/brooklyn-view.js |  74 ++++
 .../src/main/webapp/assets/js/libs/brooklyn.js  |  15 +-
 usage/jsgui/src/main/webapp/assets/js/router.js |  61 ++--
 .../src/main/webapp/assets/js/view/catalog.js   | 359 +++++++++++++------
 .../webapp/assets/js/view/effector-invoke.js    |   2 +-
 .../assets/tpl/catalog/add-catalog-entry.html   |  33 ++
 .../webapp/assets/tpl/catalog/add-entity.html   |  30 ++
 .../assets/tpl/catalog/details-entity.html      |  10 +-
 .../assets/tpl/catalog/details-generic.html     |  27 +-
 .../webapp/assets/tpl/catalog/nav-entry.html    |   2 +-
 .../main/webapp/assets/tpl/catalog/page.html    |  53 +--
 usage/jsgui/src/test/javascript/config.txt      |   1 +
 .../src/test/javascript/specs/brooklyn-spec.js  |  81 +++++
 .../javascript/specs/brooklyn-utils-spec.js     |  51 ++-
 .../main/java/brooklyn/rest/api/CatalogApi.java |  15 +-
 18 files changed, 632 insertions(+), 224 deletions(-)
----------------------------------------------------------------------



[2/9] git commit: Simplifies showing catalogue details a bit.

Posted by sj...@apache.org.
Simplifies showing catalogue details a bit.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/cf1775be
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/cf1775be
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/cf1775be

Branch: refs/heads/master
Commit: cf1775bef90532be3f365ece3ceed8978f1c3105
Parents: 68ae235
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Fri Aug 8 14:52:08 2014 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Aug 13 17:23:16 2014 +0100

----------------------------------------------------------------------
 .../src/main/webapp/assets/js/view/catalog.js   | 203 +++++++++----------
 .../webapp/assets/tpl/catalog/nav-entry.html    |   2 +-
 .../main/webapp/assets/tpl/catalog/page.html    |   5 -
 3 files changed, 96 insertions(+), 114 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf1775be/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
index 6f8d059..0e89c58 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
@@ -30,11 +30,14 @@ define([
 ], function(_, $, Backbone, FormatJSON, Location, Entity, AddLocationModalView,
         CatalogPageHtml, DetailsEntityHtml, DetailsGenericHtml, EntryHtml, LocationDetailsHtml) {
 
-    // Holds the currently active details type, e.g. applications, policies
-    var activeDetails;
+    // Holds the currently active details type, e.g. applications, policies. Bit of a workaround
+    // to share the active view with all instances of AccordionItemView, so clicking the 'reload
+    // catalog' button (handled by the parent of the AIVs) does not apply the 'active' class to
+    // more than one element.
+    var activeDetailsView;
 
     // TODO: Loading item's details should perform page navigation
-    var DetailsView = Backbone.View.extend({
+    var CatalogItemDetailsView = Backbone.View.extend({
 
         events: {
             "click .delete": "deleteItem"
@@ -42,36 +45,50 @@ define([
 
         initialize: function() {
             _.bindAll(this);
+            this.options.template = _.template(this.options.template || DetailsGenericHtml);
         },
 
-        render: function(extraMessage) {
+        render: function() {
+            if (!this.options.model) {
+                return this.renderEmpty();
+            } else {
+                return this.renderDetails();
+            }
+        },
+
+        renderEmpty: function(extraMessage) {
             this.$el.html("<div class='catalog-details'>" +
                 "<h3>Select an entry on the left</h3>" +
                 (extraMessage ? extraMessage : "") +
                 "</div>");
+            return this;
         },
 
-        show: function(model, template) {
-            // Keep the previously open section open between items
-            var open = this.$(".in").attr("id");
-            var newHtml = $(template({model: model}));
-            $(newHtml).find("#"+open).addClass("in");
-            this.$el.html(newHtml);
-
-            // rewire events. previous callbacks are removed automatically.
-            this.delegateEvents()
-        },
+        renderDetails: function() {
+            var that = this,
+                model = this.options.model,
+                template = this.options.template;
+            var show = function() {
+                // Keep the previously open section open between items. Duplication between
+                // here and setDetailsView, below. This case handles view refreshes from this
+                // view directly (e.g. when indicating an error), below handles keeping the
+                // right thing open when navigating from view to view.
+                var open = this.$(".in").attr("id");
+                var newHtml = $(template({model: model}));
+                $(newHtml).find("#"+open).addClass("in");
+                that.$el.html(newHtml);
+                // rewire events. previous callbacks are removed automatically.
+                that.delegateEvents()
+            };
 
-        showDetailsFor: function(model, template) {
             this.activeModel = model;
-            var that = this;
             // Load the view with currently available data and refresh once the load is complete.
             // Only refreshes the view if the model changes and the user hasn't selected another
             // item while the load was executing.
-            this.show(model, template);
+            show();
             model.on("change", function() {
                 if (that.activeModel.cid === model.cid) {
-                    that.show(model, template);
+                    show();
                 }
             });
             model.fetch()
@@ -79,13 +96,14 @@ define([
                     console.log("error loading", model.id, ":", errorThrown);
                     if (that.activeModel.cid === model.cid) {
                         model.error = true;
-                        that.show(model, template);
+                        show();
                     }
                 })
                 // Runs after the change event fires, or after the xhr completes
                 .always(function () {
                     model.off("change");
                 });
+            return this;
         },
 
         deleteItem: function(event) {
@@ -94,7 +112,7 @@ define([
             // removal. Useful if delete fails for e.g. lack of entitlement.
             this.activeModel.destroy();
             var displayName = $(event.currentTarget).data("name");
-            this.render(displayName ? "Deleted " + displayName : "");
+            this.renderEmpty(displayName ? "Deleted " + displayName : "");
         }
     });
 
@@ -111,10 +129,6 @@ define([
         }
     });
 
-    var accordionBodyTemplate = _.template(
-        "<div class='accordion-head capitalized'><%= name %></div>" +
-        "<div class='accordion-body' style='display: <%= display %>'></div>");
-
     /** Use to fill single accordion view list. */
     var AccordionItemView = Backbone.View.extend({
         tag: "div",
@@ -123,21 +137,25 @@ define([
             'click .accordion-head': 'toggle',
             'click .accordion-nav-row': 'showDetails'
         },
+        bodyTemplate: _.template(
+            "<div class='accordion-head capitalized'><%= name %></div>" +
+            "<div class='accordion-body' style='display: <%= display %>'></div>"),
 
         initialize: function() {
             _.bindAll(this);
             this.name = this.options.name;
             if (!this.name) {
                 throw new Error("Name should have been given for accordion entry");
+            } else if (!this.options.onItemSelected) {
+                throw new Error("onItemSelected(model, element) callback should have been given for accordion entry");
             }
 
             // Generic templates
             this.template = _.template(this.options.template || EntryHtml);
-            this.detailsTemplate = _.template(this.options.detailsTemplate || DetailsGenericHtml);
 
             // Returns template applied to function arguments. Alter if collection altered.
             // Will be run in the context of the AccordionItemView.
-            this.templateArgs = this.options.templateArgs || function(model, index) {
+            this.entryTemplateArgs = this.options.entryTemplateArgs || function(model, index) {
                 return {type: model.get("type"), id: model.get("id")};
             };
 
@@ -160,7 +178,7 @@ define([
         },
 
         render: function() {
-            this.$el.html(accordionBodyTemplate({
+            this.$el.html(this.bodyTemplate({
                 name: this.name,
                 display: this.options.autoOpen ? "block" : "none"
             }));
@@ -169,38 +187,32 @@ define([
         },
 
         renderEntries: function() {
+            var name = this.name, active = this.activeCid;
             var templater = function(model, index) {
-                var args = _.extend({cid: model.cid}, this.templateArgs(model));
+                var args = _.extend({
+                        cid: model.cid,
+                        extraClasses: (activeDetailsView == name && model.cid == active) ? "active" : ""
+                    }, this.entryTemplateArgs(model));
                 return this.template(args);
             };
             var elements = this.collection.map(templater, this);
             this.$(".accordion-body")
                 .empty()
                 .append(elements.join(''));
-            // Rehighlight active model
-            if (this.activeCid && activeDetails === this.name) {
-                $(".accordion-nav-row").removeClass("active");
-                this.setActiveItem(this.$("[data-cid='"+this.activeCid+"'"));
-            }
         },
 
         refresh: function() {
             this.collection.fetch();
         },
 
-        setActiveItem: function($element) {
-            $(".accordion-nav-row").removeClass("active");
-            $element.addClass("active");
-            activeDetails = this.name;
-        },
-
         showDetails: function(event) {
             var $event = $(event.currentTarget);
-            if (!$event.hasClass("active")) {
-                this.setActiveItem($event);
-                var cid = this.activeCid = $(event.currentTarget).data("cid");
+            var cid = $event.data("cid");
+            if (activeDetailsView !== this.name || this.activeCid !== cid) {
+                activeDetailsView = this.name;
+                this.activeCid = cid;
                 var model = this.collection.get(cid);
-                this.options.details.showDetailsFor(model, this.detailsTemplate);
+                this.options.onItemSelected(model, $event);
             }
         },
 
@@ -208,15 +220,14 @@ define([
             var body = this.$(".accordion-body");
             var hidden = this.hidden = body.css("display") == "none";
             if (hidden) {
-                this.$el.addClass('active');
                 body.removeClass("hide").slideDown('fast');
             } else {
-                this.$el.removeClass('active');
                 body.slideUp('fast')
             }
         }
     });
 
+    // Controls whole page. Parent of accordion items and details view.
     var CatalogResourceView = Backbone.View.extend({
         tagName:"div",
         className:"container container-fluid",
@@ -234,50 +245,49 @@ define([
         initialize: function() {
             $(".nav1").removeClass("active");
             $(".nav1_catalog").addClass("active");
-            this.detailsView = new DetailsView();
-            this.accordion = this.options.accordion || [
-                new AccordionItemView({
+            // Important that bind happens before accordion object is created. If it happens after
+            // `this' will not be set correctly for the onItemSelected callbacks.
+            _.bindAll(this);
+            this.accordion = this.options.accordion || {
+                "applications": new AccordionItemView({
                     name: "applications",
-                    details: this.detailsView,
-                    detailsTemplate: DetailsEntityHtml,
+                    onItemSelected: _.partial(this.showCatalogItem, DetailsEntityHtml),
                     model: Entity.Model,
                     autoOpen: true
                 }),
-                new AccordionItemView({
+                "entities": new AccordionItemView({
                     name: "entities",
-                    details: this.detailsView,
-                    detailsTemplate: DetailsEntityHtml,
+                    onItemSelected: _.partial(this.showCatalogItem, DetailsEntityHtml),
                     model: Entity.Model
                 }),
-                new AccordionItemView({
-                    name: "policies",
-                    detailsTemplate: DetailsGenericHtml,
-                    details: this.detailsView
+                "policies": new AccordionItemView({
+                    onItemSelected: _.partial(this.showCatalogItem, DetailsGenericHtml),
+                    name: "policies"
                 }),
-                new AccordionItemView({
+                "locations": new AccordionItemView({
                     name: "locations",
-                    details: this.detailsView,
-                    detailsTemplate: LocationDetailsHtml,
+                    onItemSelected: _.partial(this.showCatalogItem, LocationDetailsHtml),
                     collection: this.options.locations,
-                    templateArgs: function(location, index) {
+                    entryTemplateArgs: function (location, index) {
                         return {
                             type: location.getPrettyName(),
                             id: location.getLinkByName("self")
                         };
                     }
                 })
-            ];
-            _.bindAll(this);
+            };
         },
 
         beforeClose: function() {
             _.invoke(this.accordion, 'close');
         },
 
-        render: function(eventName) {
+        render: function() {
             this.$el.html(_.template(CatalogPageHtml, {}));
-            this.detailsView.$el = this.$("#details");
-            this.detailsView.render();
+
+            // Show empty details view to start
+            this.setDetailsView(new CatalogItemDetailsView().render());
+
             var parent = this.$(".catalog-accordion-parent");
             _.each(this.accordion, function(child) {
                 parent.append(child.render().$el);
@@ -291,53 +301,30 @@ define([
         },
 
         createNewThing: function(event) {
-            var that = this;
-            if (_.contains(that.genericTabs, that.activeAccordion)) {
-                that.addNewCatalogResource(event)
-            } else if (that.activeAccordion=='locations') {
-                that.addLocation(event)
-            } else {
-                that.$('#accordion-empty-to-create-info-message').slideDown('slow').delay(2000).slideUp('slow')
-            }
-        },
 
-        addNewCatalogResource: function(event) {
-            $('#new-entity-modal').modal('show')
         },
 
-        newEntitySubmit: function(event) {
-            var $entityForm = $('#new-entity-form'),
-                $entityModal = $('#new-entity-modal'),
-                self = this;
-            $entityModal.fadeTo(500,0.5);
-            var options = {
-                url:'/v1/catalog/',
-                type:'post',
-                success: function(data) {
-                    $entityModal.modal('hide');
-                    $entityModal.fadeTo(500,1);
-                    self.refresh()
-                },
-                error: function(data) {
-                    $entityModal.fadeTo(100,1).delay(200).fadeTo(200,0.2).delay(200).fadeTo(200,1);
-                    // TODO render the error (want better feedback than this poor-man's flashing)
-                }
-            };
-            $entityForm.ajaxSubmit(options);
-            return false
-        },
-        
-        addLocation: function(event) {
-            var locationModalView = new AddLocationModalView({
-                model:new Location.Model(),
-                appRouter:this.options.appRouter
-            });
-            this.$('#new-location-modal').replaceWith(locationModalView.render().$el);
-            this.$('#new-location-modal').modal('show');
+        showCatalogItem: function(template, model, $target) {
+            this.$(".accordion-nav-row").removeClass("active");
+            $target.addClass("active");
+            var newView = new CatalogItemDetailsView({
+                model: model,
+                template: template
+            }).render();
+            this.setDetailsView(newView)
         },
 
-        deleteLocation: function(event) {
-            this.model.get(event.currentTarget['id']).destroy();
+        setDetailsView: function(view) {
+            this.$("#details").html(view.el);
+            if (this.detailsView) {
+                // Try to re-open sections that were previously visible.
+                var openedItem = this.detailsView.$(".in").attr("id");
+                if (openedItem) {
+                    view.$("#" + openedItem).addClass("in");
+                }
+                this.detailsView.close();
+            }
+            this.detailsView = view;
         }
         
     });

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf1775be/usage/jsgui/src/main/webapp/assets/tpl/catalog/nav-entry.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/catalog/nav-entry.html b/usage/jsgui/src/main/webapp/assets/tpl/catalog/nav-entry.html
index 922f254..9c6dcbf 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/catalog/nav-entry.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/catalog/nav-entry.html
@@ -16,4 +16,4 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 -->
-<div data-cid="<%= cid %>" class="accordion-nav-row"><%= type %></div>
+<div data-cid="<%= cid %>" class="accordion-nav-row <%= extraClasses %>"><%= type %></div>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf1775be/usage/jsgui/src/main/webapp/assets/tpl/catalog/page.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/catalog/page.html b/usage/jsgui/src/main/webapp/assets/tpl/catalog/page.html
index a945329..2c6a156 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/catalog/page.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/catalog/page.html
@@ -29,11 +29,6 @@ under the License.
                 </div>
             </div>
             <div class="navbar_main_wrapper">
-                <div id="accordion-empty-to-create-info-message" class="label-message hide">
-                    <div class="label-important full-width">ERROR</div>
-                    No category selected.
-                    Select the category of the item type you wish to add.
-                </div>
                 <div class="catalog-accordion-parent catalog-accordion-wrapper"></div>
             </div>
         </div>


[8/9] git commit: Remove @Consumes(json) from CatalogApi class

Posted by sj...@apache.org.
Remove @Consumes(json) from CatalogApi class

It's untrue.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/a1b6a0d6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/a1b6a0d6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/a1b6a0d6

Branch: refs/heads/master
Commit: a1b6a0d645320359ce5824292000978c11540635
Parents: 7724f12
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Tue Aug 12 15:08:49 2014 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Aug 13 17:23:17 2014 +0100

----------------------------------------------------------------------
 .../src/main/java/brooklyn/rest/api/CatalogApi.java  | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1b6a0d6/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java b/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java
index 0642612..0888e90 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java
@@ -39,7 +39,6 @@ import java.util.List;
 @Path("/v1/catalog")
 @Apidoc("Catalog")
 @Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
 public interface CatalogApi {
 
     @POST
@@ -82,9 +81,9 @@ public interface CatalogApi {
     @ApiOperation(value = "List available entity types optionally matching a query", responseClass = "CatalogItemSummary", multiValueResponse = true)
     public List<CatalogItemSummary> listEntities(
         @ApiParam(name = "regex", value = "Regular expression to search for")
-        final @QueryParam("regex") @DefaultValue("") String regex,
+        @QueryParam("regex") @DefaultValue("") String regex,
         @ApiParam(name = "fragment", value = "Substring case-insensitive to search for")
-        final @QueryParam("fragment") @DefaultValue("") String fragment
+        @QueryParam("fragment") @DefaultValue("") String fragment
     ) ;
 
     @GET
@@ -92,9 +91,9 @@ public interface CatalogApi {
     @ApiOperation(value = "Fetch a list of application templates optionally matching a query", responseClass = "CatalogItemSummary", multiValueResponse = true)
     public List<CatalogItemSummary> listApplications(
             @ApiParam(name = "regex", value = "Regular expression to search for")
-            final @QueryParam("regex") @DefaultValue("") String regex,
+            @QueryParam("regex") @DefaultValue("") String regex,
             @ApiParam(name = "fragment", value = "Substring case-insensitive to search for")
-            final @QueryParam("fragment") @DefaultValue("") String fragment
+            @QueryParam("fragment") @DefaultValue("") String fragment
     ) ;
 
     @GET
@@ -122,9 +121,9 @@ public interface CatalogApi {
     @ApiOperation(value = "List available policies optionally matching a query", responseClass = "CatalogItemSummary", multiValueResponse = true)
     public List<CatalogItemSummary> listPolicies(
             @ApiParam(name = "regex", value = "Regular expression to search for")
-            final @QueryParam("regex") @DefaultValue("") String regex,
+            @QueryParam("regex") @DefaultValue("") String regex,
             @ApiParam(name = "fragment", value = "Substring case-insensitive to search for")
-            final @QueryParam("fragment") @DefaultValue("") String fragment
+            @QueryParam("fragment") @DefaultValue("") String fragment
     ) ;
     
     @GET
@@ -146,7 +145,7 @@ public interface CatalogApi {
     @Produces("application/image")
     public Response getIcon(
         @ApiParam(name = "itemId", value = "ID of catalog item (application, entity, policy)")
-        final @PathParam("itemId") @DefaultValue("") String itemId
+        @PathParam("itemId") @DefaultValue("") String itemId
     ) ;
 
 }


[6/9] git commit: jsgui: Adds brooklyn views module

Posted by sj...@apache.org.
jsgui: Adds brooklyn views module

Contains GenericForm object that binds inputs to given model


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/002f70f3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/002f70f3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/002f70f3

Branch: refs/heads/master
Commit: 002f70f35c20a55e2f41cbdcb4cec40870ea7c0f
Parents: a1b6a0d
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Tue Aug 12 15:10:43 2014 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Aug 13 17:23:17 2014 +0100

----------------------------------------------------------------------
 usage/jsgui/src/main/webapp/assets/js/config.js |  1 +
 .../main/webapp/assets/js/libs/brooklyn-view.js | 74 ++++++++++++++++++
 .../src/main/webapp/assets/js/libs/brooklyn.js  | 15 ++--
 usage/jsgui/src/test/javascript/config.txt      |  1 +
 .../src/test/javascript/specs/brooklyn-spec.js  | 81 ++++++++++++++++++++
 5 files changed, 166 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/002f70f3/usage/jsgui/src/main/webapp/assets/js/config.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/config.js b/usage/jsgui/src/main/webapp/assets/js/config.js
index 6a7d6df..8cff069 100644
--- a/usage/jsgui/src/main/webapp/assets/js/config.js
+++ b/usage/jsgui/src/main/webapp/assets/js/config.js
@@ -39,6 +39,7 @@ require.config({
         "moment":"libs/moment.min",
         "handlebars":"libs/handlebars-1.0.rc.1",
         "brooklyn":"libs/brooklyn",
+        "brooklyn-view":"libs/brooklyn-view",
         "brooklyn-utils":"libs/brooklyn-utils",
         "datatables-extensions":"libs/dataTables.extensions",
         "googlemaps":"view/googlemaps",

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/002f70f3/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-view.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-view.js b/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-view.js
new file mode 100644
index 0000000..8965e81
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-view.js
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+define([
+    "jquery", "underscore", "backbone", "brooklyn-utils"
+], function (
+    $, _, Backbone, Util
+) {
+
+    /**
+     * A form that listens to modifications to its inputs, maintaining a model that is
+     * submitted when a button with class 'submit' is clicked.
+     */
+    var GenericForm = Backbone.View.extend({
+        events: {
+            "change": "onChange",
+            "submit": "onSubmit"
+        },
+
+        initialize: function() {
+            if (!this.options.template) {
+                throw new Error("template required by GenericForm");
+            } else if (!this.options.onSubmit) {
+                throw new Error("onSubmit function required by GenericForm");
+            }
+            this.onSubmitCallback = this.options.onSubmit;
+            this.template = _.template(this.options.template);
+            this.model = new (this.options.model || Backbone.Model);
+            _.bindAll(this, "onSubmit", "onChange");
+            this.render();
+        },
+
+        render: function() {
+            this.$el.html(this.template());
+            // Initialise the model with existing values
+            Util.bindModelFromForm(this.model, this.$el);
+            return this;
+        },
+
+        onChange: function(e) {
+            var target = $(e.target);
+            var name = target.attr("name");
+            this.model.set(name, Util.inputValue(target), { silent: true });
+        },
+
+        onSubmit: function(e) {
+            e.preventDefault();
+            // TODO: Could validate model
+            this.onSubmitCallback(this.model);
+            return false;
+        }
+
+    });
+
+    return {
+        Form: GenericForm
+    };
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/002f70f3/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn.js b/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn.js
index d8abd14..9594d2d 100644
--- a/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn.js
+++ b/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn.js
@@ -1,6 +1,7 @@
 /** Client configuration. */
 define([
-], function () {
+    "brooklyn-view", "brooklyn-utils"
+], function (BrooklynViews, BrooklynUtils) {
 
     /**
      * Makes the console API safe to use:
@@ -38,13 +39,15 @@ define([
         }
     })();
 
-    var Config = {
-        "refresh":true,
-        "toggleRefresh":function () {
+    var Brooklyn = {
+        refresh: true,
+        toggleRefresh: function () {
             this.refresh = !this.refresh;
             return this.refresh;
-        }
+        },
+        view: BrooklynViews,
+        util: BrooklynUtils
     };
 
-    return Config;
+    return Brooklyn;
 });

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/002f70f3/usage/jsgui/src/test/javascript/config.txt
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/test/javascript/config.txt b/usage/jsgui/src/test/javascript/config.txt
index 8bf0b2b..369c404 100644
--- a/usage/jsgui/src/test/javascript/config.txt
+++ b/usage/jsgui/src/test/javascript/config.txt
@@ -16,6 +16,7 @@
         "moment":"js/libs/moment.min",
         "handlebars":"js/libs/handlebars-1.0.rc.1",
         "brooklyn":"js/libs/brooklyn",
+        "brooklyn-view":"js/libs/brooklyn-view",
         "brooklyn-utils":"js/libs/brooklyn-utils",
         "datatables-extensions":"js/libs/dataTables.extensions",
         "googlemaps":"view/googlemaps",

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/002f70f3/usage/jsgui/src/test/javascript/specs/brooklyn-spec.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/test/javascript/specs/brooklyn-spec.js b/usage/jsgui/src/test/javascript/specs/brooklyn-spec.js
new file mode 100644
index 0000000..8c9642d
--- /dev/null
+++ b/usage/jsgui/src/test/javascript/specs/brooklyn-spec.js
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+define([
+    "brooklyn", "backbone"
+], function (B, Backbone) {
+
+    describe("view", function () {
+        describe("form", function() {
+            var formTemplate = '<form>' +
+                '<input name="id" type="text"/>' +
+                '<input name="initialvalue" type="text" value="present"/>' +
+                '<button type="submit" class="submit">Submit</button>' +
+                '</form>';
+
+            it("should set existing values on the model", function() {
+                var form = new B.view.Form({template: formTemplate, onSubmit: function() {}});
+                expect(form.model.get("initialvalue")).toBe("present");
+                expect(form.model.get("id")).toBe("");
+            });
+
+            it("should maintain a model as inputs change", function () {
+                var form = new B.view.Form({
+                    template: formTemplate,
+                    onSubmit: function() {}
+                });
+                // simulate id entry
+                form.$("[name=id]").val("987");
+                form.$("[name=id]").trigger("change");
+                expect(form.model.get("id")).toBe("987");
+            });
+
+            it("should call the onSubmit callback when the form is submitted", function () {
+                var wasCalled = false;
+                var onSubmit = function (model) {
+                    wasCalled = true;
+                };
+                var form = new B.view.Form({
+                    template: formTemplate,
+                    onSubmit: onSubmit
+                });
+                console.log(form.$(".submit"));
+                form.$("form").trigger("submit");
+                expect(wasCalled).toBe(true);
+            });
+
+            // todo: expect failure jasmine method?
+            it("should fail if called without template or onSubmit", function () {
+                try {
+                    new B.view.Form({template: ""});
+                    fail;
+                } catch (e) {
+                    // expected
+                }
+                try {
+                    new B.view.Form({onSubmit: function() {}});
+                    fail;
+                } catch (e) {
+                    // expected
+                }
+
+            });
+        });
+
+    });
+});
\ No newline at end of file