You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2016/02/01 18:51:41 UTC

[08/50] [abbrv] brooklyn-ui git commit: improve workflow for adding application

improve workflow for adding application


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

Branch: refs/heads/0.5.0
Commit: b04120503127954b83caf77dc99dfcaaf927ca1b
Parents: 73881a0
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Nov 19 14:54:05 2012 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Nov 27 15:46:47 2012 -0800

----------------------------------------------------------------------
 .../src/main/webapp/assets/css/prettybrook.css  |  12 +-
 .../assets/js/view/application-add-wizard.js    | 405 ++++++++++++++++++
 .../assets/js/view/application-explorer.js      |   6 +-
 .../src/main/webapp/assets/js/view/home.js      |   6 +-
 .../main/webapp/assets/js/view/modal-wizard.js  | 410 -------------------
 .../tpl/app-add-wizard/create-config-entry.html |   8 +
 .../tpl/app-add-wizard/create-entity-entry.html |  44 ++
 .../assets/tpl/app-add-wizard/create.html       |  62 +++
 .../app-add-wizard/deploy-location-option.html  |   3 +
 .../tpl/app-add-wizard/deploy-location-row.html |   6 +
 .../assets/tpl/app-add-wizard/deploy.html       |  28 ++
 .../assets/tpl/app-add-wizard/modal-wizard.html |  13 +
 .../assets/tpl/app-add-wizard/preview.html      |  10 +
 .../webapp/assets/tpl/home/modal-wizard.html    |  13 -
 .../assets/tpl/home/step1-location-option.html  |   3 -
 .../assets/tpl/home/step1-location-row.html     |   6 -
 .../src/main/webapp/assets/tpl/home/step1.html  |  28 --
 .../assets/tpl/home/step2-config-entry.html     |   6 -
 .../assets/tpl/home/step2-entity-entry.html     |  44 --
 .../src/main/webapp/assets/tpl/home/step2.html  |  64 ---
 .../src/main/webapp/assets/tpl/home/step3.html  |  10 -
 .../specs/view/application-add-wizard-spec.js   | 194 +++++++++
 .../javascript/specs/view/modal-wizard-spec.js  | 194 ---------
 23 files changed, 785 insertions(+), 790 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/css/prettybrook.css
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/css/prettybrook.css b/usage/jsgui/src/main/webapp/assets/css/prettybrook.css
index 73a1433..6a4a7b2 100644
--- a/usage/jsgui/src/main/webapp/assets/css/prettybrook.css
+++ b/usage/jsgui/src/main/webapp/assets/css/prettybrook.css
@@ -491,26 +491,26 @@ div.for-empty-table {
     -moz-border-radius: 4px;
     border-radius: 4px;
 }
-.step2-entity-config input {
+.app-add-wizard-create-entity-config input {
     margin-bottom: 0px;	
 }
-.step2-entity-label-newline {
+.app-add-wizard-create-entity-label-newline {
     padding-left: 2px;
     padding-bottom: 3px;	
 }
-.step2-entity-label {
+.app-add-wizard-create-entity-label {
     width: 4em;
     float: left;
     padding-top: 3px;
 }
-.step2-entity-input {
+.app-add-wizard-create-entity-input {
 	width: 300px;
 }
-.step2-entity-config {
+.app-add-wizard-create-entity-config {
 	margin-bottom: 9px;
 	margin-top: 2px;
 }
-.step2-entity-config button {
+.app-add-wizard-create-entity-config button {
 	margin-left: 8px;
 }
 #add-app-entity {

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/js/view/application-add-wizard.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/application-add-wizard.js b/usage/jsgui/src/main/webapp/assets/js/view/application-add-wizard.js
new file mode 100644
index 0000000..2370878
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/js/view/application-add-wizard.js
@@ -0,0 +1,405 @@
+/**
+ * Builds a Twitter Bootstrap modal as the framework for a Wizard.
+ * Also creates an empty Application model.
+ */
+define([
+    "underscore", "jquery", "backbone", "model/entity", "model/application", "formatJson",
+    "model/location", "text!tpl/app-add-wizard/modal-wizard.html",
+    
+    "text!tpl/app-add-wizard/create.html", 
+    "text!tpl/app-add-wizard/create-entity-entry.html", "text!tpl/app-add-wizard/create-config-entry.html",
+    
+    "text!tpl/app-add-wizard/deploy.html", 
+    "text!tpl/app-add-wizard/deploy-location-row.html", "text!tpl/app-add-wizard/deploy-location-option.html",
+    
+    "text!tpl/app-add-wizard/preview.html",
+    
+    "bootstrap"
+    
+], function (_, $, Backbone, Entity, Application, FormatJSON, Location, ModalHtml, 
+		CreateHtml, 
+		CreateEntityEntryHtml, CreateConfigEntryHtml,
+		DeployHtml, 
+		DeployLocationRowHtml, DeployLocationOptionHtml,  
+		PreviewHtml
+		) {
+
+    var ModalWizard = Backbone.View.extend({
+        tagName:'div',
+        className:'modal hide fade',
+        events:{
+            'click #next_step':'nextStep',
+            'click #prev_step':'prevStep'
+        },
+        template:_.template(ModalHtml),
+        initialize:function () {
+            this.model = new Application.Spec
+            this.currentStep = 0;
+            this.steps = [
+                          {
+                              step_id:'what-app',
+                              title:'Create Application',
+                              instructions:'Define how the application is built and the configuration parameters',
+                              view:new ModalWizard.StepCreate({ model:this.model})
+                          },
+                          {
+                              step_id:'name-and-locations',
+                              title:'Deploy Application',
+                              instructions:'Enter the name of the new application and the location(s) where you wish to deploy it.',
+                              view:new ModalWizard.StepDeploy({ model:this.model })
+                          },
+                          {
+                              step_id:'preview',
+                              title:'Application Preview',
+                              instructions:'Confirm the code which will be sent to the server, optionally tweaking it or saving it for future reference.',
+                              view:new ModalWizard.StepPreview({ model:this.model})
+                          }
+                          ]
+        },
+        beforeClose:function () {
+            // ensure we close the sub-views
+            _.each(this.steps, function (step) {
+                step.view.close()
+            }, this)
+        },
+        render:function () {
+            this.$el.html(this.template({}))
+            this.renderCurrentStep()
+            return this
+        },
+
+        renderCurrentStep:function () {
+            this.title = this.$("h3#step_title")
+            this.instructions = this.$("p#step_instructions")
+            this.nextStepButton = this.$("#next_step")
+            this.prevStepButton = this.$("#prev_step")
+
+            var currentStep = this.steps[this.currentStep]
+            this.title.html(currentStep.title)
+            this.instructions.html(currentStep.instructions)
+            this.currentView = currentStep.view
+            
+            // delegate to sub-views !!
+            this.$(".modal-body").replaceWith(this.currentView.render().el)
+
+            if (this.currentStep > 0) {
+                this.prevStepButton.html("Previous").show()
+            } else {
+                this.prevStepButton.hide()
+            }
+            
+            if (this.currentStep < 2) {
+                this.nextStepButton.html("Next")
+            } else {
+                this.nextStepButton.html("Finish")
+            }
+        },
+        submitApplication:function (event) {
+            var that = this
+            $.ajax({
+                url:'/v1/applications',
+                type:'post',
+                contentType:'application/json',
+                processData:false,
+                data:JSON.stringify(this.model.toJSON()),
+                success:function (data) {
+                    var $modal = $('#modal-container .modal')
+                    $modal.modal('hide')
+                    if (that.options.callback) that.options.callback();
+                },
+                error:function (data) {
+                	that.steps[that.currentStep].view.showFailure()
+                }
+            })
+            return false
+        },
+        // TODO prev and next not so simple anymore, are they?
+        nextStep:function () {
+            if (this.currentView.validate()) {
+                if (this.currentStep < 2) {
+                    this.currentStep += 1
+                    this.renderCurrentStep()
+                } else {
+                    this.submitApplication()
+                }
+            }
+        },
+        prevStep:function () {
+            this.currentStep -= 1
+            this.renderCurrentStep()
+        }
+    })
+    
+        // Note: this does not restore values on a back click; setting type and entity type+name is easy,
+    // but relevant config lines is a little bit more tedious
+    ModalWizard.StepCreate = Backbone.View.extend({
+        className:'modal-body',
+        events:{
+            'click #add-app-entity':'addEntity',
+            'click .editable-entity-heading':'expandEntity',
+            'click .remove-entity-button':'removeEntityClick',
+            'click .editable-entity-button':'saveEntityClick',
+            'click #remove-config':'removeConfigRow',
+            'click #add-config':'addConfigRow'
+        },
+        template:_.template(CreateHtml),
+        initialize:function () {
+            var self = this
+            self.catalogEntities = []
+            self.catalogApplications = []
+            
+            this.$el.html(this.template({}))
+            this.addEntity()
+            
+            $.get('/v1/catalog/entities', {}, function (result) {
+                self.catalogEntities = result
+                self.$(".entity-type-input").typeahead().data('typeahead').source = self.catalogEntities
+            })
+            $.get('/v1/catalog/applications', {}, function (result) {
+                self.catalogApplications = result
+                self.$(".application-type-input").typeahead().data('typeahead').source = self.catalogApplications
+            })
+        },
+        beforeClose:function () {
+        },
+        renderConfiguredEntities:function () {
+            var $configuredEntities = this.$('#entitiesAccordionish').empty()
+            var that = this
+            if (this.model.get("entities").length > 0) {
+                _.each(this.model.get("entities"), function (entity) {
+                    that.addEntityHtml($configuredEntities, entity)
+                })
+            }
+        },
+        
+        render:function () {
+            this.renderConfiguredEntities()
+            this.delegateEvents()
+            return this
+        },
+        
+        expandEntity:function (event) {
+            $(event.currentTarget).next().show('fast').delay(1000).prev().hide('slow')
+        },
+        saveEntityClick:function (event) {
+            this.saveEntity($(event.currentTarget).parent().parent().parent());
+        },
+        saveEntity:function ($entityGroup) {
+            var that = this
+            var name = $('#entity-name',$entityGroup).val()
+            var type = $('#entity-type',$entityGroup).val()
+            if (type=="" || !_.contains(that.catalogEntities, type)) {
+                $('.entity-info-message',$entityGroup).show('slow').delay(2000).hide('slow')
+                return false
+            }
+            var saveTarget = this.model.get("entities")[$entityGroup.index()];
+            this.model.set("type", null)
+            saveTarget.name = name
+            saveTarget.type = type
+            saveTarget.config = this.getConfigMap($entityGroup)
+            
+            if (name=="") name=type;
+            if (name=="") name="<i>(new entity)</i>";
+            $('#entity-name-header',$entityGroup).html( name )
+            $('.editable-entity-body',$entityGroup).prev().show('fast').next().hide('fast')
+            return true;
+        },
+        getConfigMap:function (root) {
+            var map = {}
+            $('.app-add-wizard-create-entity-config',root).each( function (index,elt) {
+                map[$('#key',elt).val()] = $('#value',elt).val()
+            })
+            return map;
+        },
+        saveTemplate:function () {
+            var that = this
+            var tab = $.find('#templateTab')
+            var type = $(tab).find('#entity-type').val()
+            if (!_.contains(this.catalogApplications, type)) {
+                $('.entity-info-message').show('slow').delay(2000).hide('slow')
+                return false
+            }
+            this.model.set("type", type);
+            this.model.set("config", this.getConfigMap(tab))
+            return true;
+        },
+        addEntity:function () {
+            var entity = new Entity.Model
+            this.model.addEntity( entity )
+            this.addEntityHtml(this.$('#entitiesAccordionish'), entity)
+        },
+        addEntityHtml:function (parent, entity) {
+            var $entity = _.template(CreateEntityEntryHtml, {})
+            var that = this
+            parent.append($entity)
+            parent.children().last().find('.entity-type-input').typeahead({ source: that.catalogEntities })
+        },        
+        removeEntityClick:function (event) {
+            var $entityGroup = $(event.currentTarget).parent().parent().parent();
+            this.model.removeEntityIndex($entityGroup.index())
+            $entityGroup.remove()
+        },
+        
+        addConfigRow:function (event) {
+            var $row = _.template(CreateConfigEntryHtml, {})
+            $(event.currentTarget).parent().prev().append($row)
+        },
+        removeConfigRow:function (event) {
+            $(event.currentTarget).parent().remove()
+        },
+        
+        validate:function () {
+            var that = this
+            var tabName = $('#app-add-wizard-create-tab li[class="active"] a').attr('href')
+            if (tabName=='#entitiesTab') {
+                var allokay = true
+                $($.find('.editable-entity-group')).each(
+                    function (i,$entityGroup) {
+                        allokay = that.saveEntity($entityGroup) & allokay
+                    })
+                if (!allokay) return false;
+                if (this.model.get("entities").length > 0) {
+                    this.model.set("type", null);
+                    return true;
+                }
+            } else if (tabName=='#templateTab') {
+                if (this.saveTemplate()) {
+                    this.model.set("entities", []);
+                    return true
+                }
+            } else {
+                // other tabs not implemented yet 
+                // do nothing, show error return false below
+            }
+            this.$('div.app-add-wizard-create-info-message').show('slow').delay(2000).hide('slow')
+            return false
+        }
+
+    })
+
+    ModalWizard.StepDeploy = Backbone.View.extend({
+        className:'modal-body',
+        events:{
+            'click #add-selector-container':'addLocation',
+            'click #remove-app-location':'removeLocation',
+            'change select':'selection',
+            'change option':'selection',
+            'blur #application-name':'updateName'
+        },
+        template:_.template(DeployHtml),
+        locationRowTemplate:_.template(DeployLocationRowHtml),
+        locationOptionTemplate:_.template(DeployLocationOptionHtml),
+
+        initialize:function () {
+            this.model.on("change", this.render, this)
+            this.$el.html(this.template({}))
+            this.locations = new Location.Collection()
+        },
+        beforeClose:function () {
+            this.model.off("change", this.render)
+        },
+        renderName:function () {
+            this.$('#application-name').val(this.model.get("name"))
+        },
+        renderAddedLocations:function () {
+            // renders the locations added to the model
+        	var that = this;
+        	var container = this.$("#selector-container")
+        	container.empty()
+        	for (var li = 0; li < this.model.get("locations").length; li++) {
+        		var chosenLocation = this.model.get("locations")[li];
+        		container.append(that.locationRowTemplate({
+        				initialValue: chosenLocation,
+        				rowId: li
+        			}))
+        	}
+    		var $selectLocations = container.find('#select-location')
+    		this.locations.each(function(aLocation) {
+        			var $option = that.locationOptionTemplate({
+                        url:aLocation.getLinkByName("self"),
+                        name:aLocation.getPrettyName()
+                    })
+                    $selectLocations.append($option)
+        		})
+    		$selectLocations.each(function(i) {
+    			var url = $($selectLocations[i]).parent().attr('initialValue');
+    			$($selectLocations[i]).val(url)
+    		})
+        },
+        render:function () {
+        	var that = this
+            this.renderName()
+            this.locations.fetch({async:false,
+                success:function () {
+                	if (that.model.get("locations").length==0)
+                		that.addLocation()
+            		else
+            			that.renderAddedLocations()
+                }})
+            this.delegateEvents()
+            return this
+        },
+        addLocation:function () {
+        	if (this.locations.models.length>0) {
+            	this.model.addLocation(this.locations.models[0].getLinkByName("self"))
+            	this.renderAddedLocations()
+        	} else {
+                this.$('div.info-nolocs-message').show('slow').delay(2000).hide('slow')
+        	}
+        },
+        removeLocation:function (event) {
+            var toBeRemoved = $(event.currentTarget).parent().attr('rowId')
+            this.model.removeLocationIndex(toBeRemoved)
+            this.renderAddedLocations()
+        },
+        selection:function (event) {
+        	var url = $(event.currentTarget).val();
+        	var loc = this.locations.find(function (candidate) {
+        		return candidate.getLinkByName("self")==url
+    		})
+        	this.model.setLocationAtIndex($(event.currentTarget).parent().attr('rowId'), 
+        			loc.getLinkByName("self"))
+        },
+        updateName:function () {
+            this.model.set("name", this.$('#application-name').val())
+        },
+        validate:function () {
+            if (this.model.get("name") !== "" && this.model.get("locations").length !== 0) {
+                return true
+            }
+            this.$('div.info-message').show('slow').delay(2000).hide('slow')
+            return false
+        }
+    })
+
+    ModalWizard.StepPreview = Backbone.View.extend({
+        className:'modal-body',
+        initialize:function () {
+            this.$el.html(_.template(PreviewHtml))
+            this.model.on("change", this.render, this)
+        },
+        beforeClose:function () {
+            this.model.off("change", this.render)
+        },
+        render:function () {
+            this.$('#app-summary').val(FormatJSON(this.model.toJSON()))
+            this.delegateEvents()
+            return this
+        },
+        validate:function () {
+            if (this.model.get("name") != ""
+                && this.model.get("locations").length > 0
+                && (this.model.get("type")!=null || 
+                		this.model.get("entities").length > 0)) {
+                return true
+            }
+            this.showFailure()
+            return false
+        },
+        showFailure:function () {
+        	this.$('div.info-message').show('slow').delay(2000).hide('slow')
+        }
+    })
+
+    return ModalWizard
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js b/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js
index 4c7ff02..3ebf74f 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js
@@ -5,9 +5,9 @@
  */
 define([
     "underscore", "jquery", "backbone", 
-    "./modal-wizard", "model/app-tree", "./application-tree", 
+    "./application-add-wizard", "model/app-tree", "./application-tree", 
     "text!tpl/apps/page.html"
-], function (_, $, Backbone, ModalWizard, AppTree, ApplicationTreeView, PageHtml) {
+], function (_, $, Backbone, AppAddWizard, AppTree, ApplicationTreeView, PageHtml) {
 
     var ApplicationExplorerView = Backbone.View.extend({
         tagName:"div",
@@ -52,7 +52,7 @@ define([
             if (this._modal) {
                 this._modal.close()
             }
-            var wizard = new ModalWizard({
+            var wizard = new AppAddWizard({
             	appRouter:that.options.appRouter,
             	callback:function() { that.refreshApplications() }
         	})

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/js/view/home.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/home.js b/usage/jsgui/src/main/webapp/assets/js/view/home.js
index fc39bd8..67b246a 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/home.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/home.js
@@ -3,10 +3,10 @@
  */
 
 define([
-    "underscore", "jquery", "backbone", "./modal-wizard", "model/location",
+    "underscore", "jquery", "backbone", "./application-add-wizard", "model/location",
     "text!tpl/home/applications.html", "text!tpl/home/summaries.html", "text!tpl/home/app-entry.html", 
     "bootstrap"
-], function (_, $, Backbone, ModalWizard, Location, ApplicationsHtml, HomeSummariesHtml, AppEntryHtml) {
+], function (_, $, Backbone, AppAddWizard, Location, ApplicationsHtml, HomeSummariesHtml, AppEntryHtml) {
 
     var HomeView = Backbone.View.extend({
         tagName:"div",
@@ -115,7 +115,7 @@ define([
                 this._modal.close()
             }
             var that = this;
-            var wizard = new ModalWizard({appRouter:this.options.appRouter})
+            var wizard = new AppAddWizard({appRouter:this.options.appRouter})
             this._modal = wizard
             this.$("#modal-container").html(wizard.render().el)
             this.$("#modal-container .modal")

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/js/view/modal-wizard.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/modal-wizard.js b/usage/jsgui/src/main/webapp/assets/js/view/modal-wizard.js
deleted file mode 100644
index bc007cb..0000000
--- a/usage/jsgui/src/main/webapp/assets/js/view/modal-wizard.js
+++ /dev/null
@@ -1,410 +0,0 @@
-/**
- * Builds a Twitter Bootstrap modal as the framework for a Wizard.
- * Also creates an empty Application model.
- */
-define([
-    "underscore", "jquery", "backbone", "model/entity", "model/application", "formatJson",
-    "model/location", "text!tpl/home/modal-wizard.html", 
-    "text!tpl/home/step1.html", "text!tpl/home/step2.html", "text!tpl/home/step3.html", 
-    "text!tpl/home/step1-location-row.html", "text!tpl/home/step1-location-option.html",
-    "text!tpl/home/step2-entity-entry.html", "text!tpl/home/step2-config-entry.html", "bootstrap"
-], function (_, $, Backbone, Entity, Application, FormatJSON, Location, ModalHtml, 
-		Step1Html, Step2Html, Step3Html, 
-		Step1LocationRowHtml, LocationOptionHtml,  
-		Step2EntityEntryHtml, Step2ConfigEntryHtml) {
-
-    var ModalWizard = Backbone.View.extend({
-        tagName:'div',
-        className:'modal hide fade',
-        events:{
-            'click #next_step':'nextStep',
-            'click #prev_step':'prevStep'
-        },
-        template:_.template(ModalHtml),
-        initialize:function () {
-            this.model = new Application.Spec
-            this.currentStep = 0
-            this.steps = [
-                {
-                    step_number:1,
-                    title:'Deploy Application',
-                    instructions:'Enter the name of the new application and the location(s) where you wish to deploy it.',
-                    view:new ModalWizard.Step1({ model:this.model })
-                },
-                {
-                    step_number:2,
-                    title:'Configure Application',
-                    instructions:'Define how the application is built and the configuration parameters',
-                    view:new ModalWizard.Step2({ model:this.model})
-                },
-                {
-                    step_number:3,
-                    title:'Application Summary',
-                    instructions:'Confirm and save the JSON details which will be used to create the application',
-                    view:new ModalWizard.Step3({ model:this.model})
-                }
-            ]
-        },
-        beforeClose:function () {
-            // ensure we close the sub-views
-            _.each(this.steps, function (step) {
-                step.view.close()
-            }, this)
-        },
-        render:function () {
-            this.$el.html(this.template({}))
-            this.renderCurrentStep()
-            return this
-        },
-
-        renderCurrentStep:function () {
-            this.title = this.$("h3#step_title")
-            this.instructions = this.$("p#step_instructions")
-            this.nextStepButton = this.$("#next_step")
-            this.prevStepButton = this.$("#prev_step")
-
-            var currentStep = this.steps[this.currentStep]
-            if (!this.isFirstStep()) var prevStep = this.steps[this.currentStep - 1]
-            var nextStep = this.steps[this.currentStep + 1]
-
-            this.title.html(currentStep.title)
-            this.instructions.html(currentStep.instructions)
-            this.currentView = currentStep.view
-            // delegate to sub-views !!
-            this.$(".modal-body").replaceWith(this.currentView.render().el)
-
-            if (prevStep) {
-                this.prevStepButton.html("Previous").show()
-            } else {
-                this.prevStepButton.hide()
-            }
-            if (nextStep) {
-                this.nextStepButton.html("Next")
-            } else {
-                this.nextStepButton.html("Finish")
-            }
-        },
-        submitApplication:function (event) {
-            var that = this
-            $.ajax({
-                url:'/v1/applications',
-                type:'post',
-                contentType:'application/json',
-                processData:false,
-                data:JSON.stringify(this.model.toJSON()),
-                success:function (data) {
-                    var $modal = $('#modal-container .modal')
-                    $modal.modal('hide')
-                    if (that.options.callback) that.options.callback();
-                },
-                error:function (data) {
-                	that.steps[that.currentStep].view.showFailure()
-                }
-            })
-            return false
-        },
-        nextStep:function () {
-            if (this.currentView.validate()) {
-                if (!this.isLastStep()) {
-                    this.currentStep += 1
-                    this.renderCurrentStep()
-                } else {
-                    this.submitApplication()
-                }
-            }
-        },
-        prevStep:function () {
-            if (!this.isFirstStep()) {
-                this.currentStep -= 1
-                this.renderCurrentStep()
-            }
-        },
-        isFirstStep:function () {
-            return (this.currentStep == 0)
-        },
-        isLastStep:function () {
-            return (this.currentStep == this.steps.length - 1)
-        }
-    })
-
-    /**
-     * Wizard for creating a new application. First step: assign a name and a location for the app.
-     */
-    ModalWizard.Step1 = Backbone.View.extend({
-        className:'modal-body',
-        events:{
-            'click #add-selector-container':'addLocation',
-            'click #remove-app-location':'removeLocation',
-            'change select':'selection',
-            'change option':'selection',
-            'blur #application-name':'updateName'
-        },
-        template:_.template(Step1Html),
-        locationRowTemplate:_.template(Step1LocationRowHtml),
-        locationOptionTemplate:_.template(LocationOptionHtml),
-
-        initialize:function () {
-            this.model.on("change", this.render, this)
-            this.$el.html(this.template({}))
-            this.locations = new Location.Collection()
-        },
-        beforeClose:function () {
-            this.model.off("change", this.render)
-        },
-        renderName:function () {
-            this.$('#application-name').val(this.model.get("name"))
-        },
-        renderAddedLocations:function () {
-            // renders the locations added to the model
-        	var that = this;
-        	var container = this.$("#selector-container")
-        	container.empty()
-        	for (var li = 0; li < this.model.get("locations").length; li++) {
-        		var chosenLocation = this.model.get("locations")[li];
-        		container.append(that.locationRowTemplate({
-        				initialValue: chosenLocation,
-        				rowId: li
-        			}))
-        	}
-    		var $selectLocations = container.find('#select-location')
-    		this.locations.each(function(aLocation) {
-        			var $option = that.locationOptionTemplate({
-                        url:aLocation.getLinkByName("self"),
-                        name:aLocation.getPrettyName()
-                    })
-                    $selectLocations.append($option)
-        		})
-    		$selectLocations.each(function(i) {
-    			var url = $($selectLocations[i]).parent().attr('initialValue');
-    			$($selectLocations[i]).val(url)
-    		})
-        },
-        render:function () {
-        	var that = this
-            this.renderName()
-            this.locations.fetch({async:false,
-                success:function () {
-                	if (that.model.get("locations").length==0)
-                		that.addLocation()
-            		else
-            			that.renderAddedLocations()
-                }})
-            this.delegateEvents()
-            return this
-        },
-        addLocation:function () {
-        	if (this.locations.models.length>0) {
-            	this.model.addLocation(this.locations.models[0].getLinkByName("self"))
-            	this.renderAddedLocations()
-        	} else {
-                this.$('div.info-nolocs-message').show('slow').delay(2000).hide('slow')
-        	}
-        },
-        removeLocation:function (event) {
-            var toBeRemoved = $(event.currentTarget).parent().attr('rowId')
-            this.model.removeLocationIndex(toBeRemoved)
-            this.renderAddedLocations()
-        },
-        selection:function (event) {
-        	var url = $(event.currentTarget).val();
-        	var loc = this.locations.find(function (candidate) {
-        		return candidate.getLinkByName("self")==url
-    		})
-        	this.model.setLocationAtIndex($(event.currentTarget).parent().attr('rowId'), 
-        			loc.getLinkByName("self"))
-        },
-        updateName:function () {
-            this.model.set("name", this.$('#application-name').val())
-        },
-        validate:function () {
-            if (this.model.get("name") !== "" && this.model.get("locations").length !== 0) {
-                return true
-            }
-            this.$('div.info-message').show('slow').delay(2000).hide('slow')
-            return false
-        }
-    })
-
-    /**
-     * Second step from the create application wizard. Allows you to add and new entities and configure them.
-     */
-    // Note: this does not restore values on a back click; setting type and entity type+name is easy,
-    // but relevant config lines is a little bit more tedious
-    ModalWizard.Step2 = Backbone.View.extend({
-        className:'modal-body',
-        events:{
-            'click #add-app-entity':'addEntity',
-            'click .editable-entity-heading':'expandEntity',
-            'click .remove-entity-button':'removeEntityClick',
-            'click .editable-entity-button':'saveEntityClick',
-            'click #remove-config':'removeConfigRow',
-            'click #add-config':'addConfigRow'
-        },
-        template:_.template(Step2Html),
-        initialize:function () {
-            var self = this
-            self.catalogEntities = []
-            self.catalogApplications = []
-            
-            this.$el.html(this.template({}))
-            this.addEntity()
-            
-            $.get('/v1/catalog/entities', {}, function (result) {
-                self.catalogEntities = result
-                self.$(".entity-type-input").typeahead().data('typeahead').source = self.catalogEntities
-            })
-            $.get('/v1/catalog/applications', {}, function (result) {
-                self.catalogApplications = result
-                self.$(".application-type-input").typeahead().data('typeahead').source = self.catalogApplications
-            })
-        },
-        beforeClose:function () {
-        },
-        renderConfiguredEntities:function () {
-            var $configuredEntities = this.$('#entitiesAccordionish').empty()
-            var that = this
-            if (this.model.get("entities").length > 0) {
-                _.each(this.model.get("entities"), function (entity) {
-                	that.addEntityHtml($configuredEntities, entity)
-                })
-            }
-        },
-        
-        render:function () {
-            this.renderConfiguredEntities()
-            this.delegateEvents()
-            return this
-        },
-        
-        expandEntity:function (event) {
-        	$(event.currentTarget).next().show('fast').delay(1000).prev().hide('slow')
-        },
-        saveEntityClick:function (event) {
-        	this.saveEntity($(event.currentTarget).parent().parent().parent());
-        },
-        saveEntity:function ($entityGroup) {
-        	var that = this
-        	var name = $('#entity-name',$entityGroup).val()
-        	var type = $('#entity-type',$entityGroup).val()
-        	if (type=="" || !_.contains(that.catalogEntities, type)) {
-        		$('.entity-info-message',$entityGroup).show('slow').delay(2000).hide('slow')
-        		return false
-        	}
-    		var saveTarget = this.model.get("entities")[$entityGroup.index()];
-    		this.model.set("type", null)
-    		saveTarget.name = name
-    		saveTarget.type = type
-    		saveTarget.config = this.getConfigMap($entityGroup)
-    		
-    		if (name=="") name=type;
-    		if (name=="") name="<i>(new entity)</i>";
-    		$('#entity-name-header',$entityGroup).html( name )
-    		$('.editable-entity-body',$entityGroup).prev().show('fast').next().hide('fast')
-        	return true;
-        },
-        getConfigMap:function (root) {
-        	var map = {}
-        	$('.step2-entity-config',root).each( function (index,elt) {
-        		map[$('#key',elt).val()] = $('#value',elt).val()
-        	})
-        	return map;
-        },
-        saveTemplate:function () {
-        	var that = this
-        	var tab = $.find('#templateTab')
-        	var type = $(tab).find('#entity-type').val()
-        	if (!_.contains(this.catalogApplications, type)) {
-        		$('.entity-info-message').show('slow').delay(2000).hide('slow')
-        		return false
-        	}
-    		this.model.set("type", type);
-    		this.model.set("config", this.getConfigMap(tab))
-        	return true;
-        },
-        addEntity:function () {
-        	var entity = new Entity.Model
-        	this.model.addEntity( entity )
-        	this.addEntityHtml(this.$('#entitiesAccordionish'), entity)
-        },
-    	addEntityHtml:function (parent, entity) {
-            var $entity = _.template(Step2EntityEntryHtml, {})
-            var that = this
-            parent.append($entity)
-            parent.children().last().find('.entity-type-input').typeahead({ source: that.catalogEntities })
-        },        
-        removeEntityClick:function (event) {
-        	var $entityGroup = $(event.currentTarget).parent().parent().parent();
-        	this.model.removeEntityIndex($entityGroup.index())
-        	$entityGroup.remove()
-        },
-        
-        addConfigRow:function (event) {
-        	var $row = _.template(Step2ConfigEntryHtml, {})
-        	$(event.currentTarget).parent().prev().append($row)
-        },
-        removeConfigRow:function (event) {
-        	$(event.currentTarget).parent().remove()
-        },
-        
-        validate:function () {
-        	var that = this
-        	var tabName = $('#step2Tab li[class="active"] a').attr('href')
-        	if (tabName=='#entitiesTab') {
-        		var allokay = true
-        		$($.find('.editable-entity-group')).each(
-    				function (i,$entityGroup) {
-    					allokay = that.saveEntity($entityGroup) & allokay
-    				})
-				if (!allokay) return false;
-        		if (this.model.get("entities").length > 0) {
-        			this.model.set("type", null);
-        			return true;
-        		}
-        	} else if (tabName=='#templateTab') {
-        		if (this.saveTemplate()) {
-        			this.model.set("entities", []);
-        			return true
-        		}
-            } else {
-            	// other tabs not implemented yet 
-            	// do nothing, show error return false below
-            }
-            this.$('div.step2-info-message').show('slow').delay(2000).hide('slow')
-            return false
-        }
-
-    })
-    /**
-     * Final step from the create application wizard. Review the summary and submit the request.
-     */
-    ModalWizard.Step3 = Backbone.View.extend({
-        className:'modal-body',
-        initialize:function () {
-            this.$el.html(_.template(Step3Html))
-            this.model.on("change", this.render, this)
-        },
-        beforeClose:function () {
-            this.model.off("change", this.render)
-        },
-        render:function () {
-            this.$('#app-summary').val(FormatJSON(this.model.toJSON()))
-            this.delegateEvents()
-            return this
-        },
-        validate:function () {
-            if (this.model.get("name") != ""
-                && this.model.get("locations").length > 0
-                && (this.model.get("type")!=null || 
-                		this.model.get("entities").length > 0)) {
-                return true
-            }
-            this.showFailure()
-            return false
-        },
-        showFailure:function () {
-        	this.$('div.info-message').show('slow').delay(2000).hide('slow')
-        }
-    })
-
-    return ModalWizard
-})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-config-entry.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-config-entry.html b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-config-entry.html
new file mode 100644
index 0000000..f4a1779
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-config-entry.html
@@ -0,0 +1,8 @@
+<div class="controls app-add-wizard-create-entity-config">
+	<input id="key" type="text" class="input-medium" name="key" placeholder="key"> 
+	<input id="value" type="text" class="input-medium" name="value" placeholder="value">
+	
+	<button id="remove-config" class="btn btn-info btn-mini" type="button">
+		<i class="icon-minus-sign"></i>
+	</button>
+</div>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-entity-entry.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-entity-entry.html b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-entity-entry.html
new file mode 100644
index 0000000..df12216
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-entity-entry.html
@@ -0,0 +1,44 @@
+<div class="editable-entity-group">
+	<div class="editable-entity-heading hide">
+        <span id="entity-name-header"><i>(new entity)</i></span>
+        <div style="float: right;">
+            <button class="btn btn-info btn-mini"><i class="icon-chevron-down"></i></button>
+        </div>
+	</div>
+	
+	<div class="editable-entity-body">
+        <div class="entity-info-message label-message hide">
+            <span class="label-important">ERROR</span>  Invalid entity/type definition
+        </div>
+        
+        <div style="float: right;">
+            <button class="btn btn-info btn-mini remove-entity-button">
+                <i class="icon-remove"></i> </button>
+            <button class="btn btn-info btn-mini editable-entity-button">
+                <i class="icon-chevron-up"></i> </button>
+        </div>
+		<div class="control-group">
+			<div class="controls">
+			    <div class="app-add-wizard-create-entity-label">Name</div>
+				<input id="entity-name" type="text" class="input-large app-add-wizard-create-entity-input" name="name" placeholder="name">
+			</div>
+		</div>
+
+		<div class="control-group">
+			<div class="controls">
+			    <div class="app-add-wizard-create-entity-label">Type</div>
+				<input id="entity-type" type="text" name="type" class="input-large app-add-wizard-create-entity-input entity-type-input" placeholder="type">
+			</div>
+		</div>
+
+		<div class="control-group">
+			<div class="app-add-wizard-create-entity-label-newline">Configuration</div>
+            <div class="controls">
+            </div>
+			<div>
+				<button id="add-config" class="btn btn-mini btn-info">
+				    Add Config Key</button>
+			</div>
+		</div>
+	</div>
+</div>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create.html b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create.html
new file mode 100644
index 0000000..0ae2c0a
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create.html
@@ -0,0 +1,62 @@
+<div class="app-add-wizard-create-info-message label-message hide">
+	<div class="label-important">ERROR</div>  Invalid application type/spec
+</div>
+
+<div id="app-add-wizard-create-body">
+
+	<ul class="nav nav-tabs" id="app-add-wizard-create-tab">
+		<li class="active"><a href="#templateTab" data-toggle="tab">Template</a></li>
+		<li><a href="#entitiesTab" data-toggle="tab">Entities</a></li>
+		
+		<li class="dropdown"><a class="dropdown-toggle" id="dropLanguage"
+			role="button" data-toggle="dropdown" href="#">Code <b class="caret"></b></a>
+			<ul id="menuLanguage" class="dropdown-menu" role="menu" aria-labelledby="dropLanguage">
+				<li><a tabindex="-1" data-toggle="tab" href="#jsonTab">JSON</a></li>
+				<li><a tabindex="-1" data-toggle="tab" href="#xmlTab">XML</a></li>
+				<li class="divider"></li>
+				<li><a tabindex="-1" data-toggle="tab" href="#groovyTab">Groovy</a></li>
+				<li class="divider"></li>
+				<li><a tabindex="-1" data-toggle="tab" href="#uploadTab">JAR</a></li>
+			</ul></li>
+	</ul>
+
+  <div class="tab-content">
+  <div class="tab-pane active" id="templateTab">
+  
+        <div class="control-group">
+            <div class="app-add-wizard-create-entity-label-newline">Type</div>
+            <div class="controls app-type">
+                <input id="entity-type" type="text" name="type" class="input-large app-add-wizard-create-entity-input application-type-input" placeholder="type">
+            </div>
+        </div>
+
+        <div class="control-group">
+            <div class="app-add-wizard-create-entity-label-newline">Configuration</div>
+            <div class="controls">
+            </div>
+            <div>
+                <button id="add-config" class="btn btn-mini btn-info">
+                    Add Config Key</button>
+            </div>
+        </div>
+  </div>
+
+  
+  <div class="tab-pane" id="entitiesTab">
+    <div id="entitiesAccordionish"></div>
+    <button id="add-app-entity" class="btn btn-info btn-mini" style="margin:5px;">Add Additional Entity</button>
+  </div>
+
+  <div class="tab-pane" id="jsonTab">
+    <br/><br/><i>coming soon!</i>
+  </div>
+  <div class="tab-pane" id="xmlTab">
+    <br/><br/><i>coming soon!</i>
+  </div>
+  <div class="tab-pane" id="groovyTab">
+    <br/><br/><i>coming soon!</i>
+  </div>
+  <div class="tab-pane" id="uploadTab">
+    <br/><br/><i>coming soon!</i>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy-location-option.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy-location-option.html b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy-location-option.html
new file mode 100644
index 0000000..9242353
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy-location-option.html
@@ -0,0 +1,3 @@
+<option value="<%= url %>">
+    <span class="provider"><%= name %></span>
+</option>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy-location-row.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy-location-row.html b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy-location-row.html
new file mode 100644
index 0000000..ed51a2a
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy-location-row.html
@@ -0,0 +1,6 @@
+<div id="location-row-<%= rowId %>" rowId="<%= rowId %>" initialValue="<%= initialValue %>" class="location-selector-row">
+    <select id="select-location" style="margin:4px 0 4px 0; width:80%"></select>
+    <% if (rowId>0) { %>
+        <button id="remove-app-location" class="btn btn-info btn-mini" type="button"><i class="icon-minus-sign"></i></button>
+    <% } %>    
+</div>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy.html b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy.html
new file mode 100644
index 0000000..b69f5ba
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy.html
@@ -0,0 +1,28 @@
+<!-- New application wizard step 1: set name and locations -->
+
+<div class="info-message label-message hide">
+        <span class="label-important">Important</span>
+        Name and location must be specified
+</div>
+<div class="info-nolocs-message label-message hide">
+        <span class="label-important">Important</span> 
+        Missing or unconfigured locations
+</div>
+
+
+<div class="control-group">
+    <label for="application-name">Name</label>
+
+    <div class="controls">
+        <input id="application-name" name="name" type="text" style="width:80%">
+    </div>
+</div>
+
+<div id="app-locations">
+    <div>Locations</div>
+    <div id="selector-container" class="control-group">
+    </div>
+    <button id="add-selector-container" class="btn btn-info btn-mini">Add Additional Location</button>
+</div>
+
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/modal-wizard.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/modal-wizard.html b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/modal-wizard.html
new file mode 100644
index 0000000..540b37a
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/modal-wizard.html
@@ -0,0 +1,13 @@
+<div class="modal-header">
+    <button type="button" class="close" data-dismiss="modal">x</button>
+    <h3 id="step_title">Title placeholder</h3>
+
+    <p id="step_instructions">Instructions placeholder</p>
+</div>
+
+<div class="modal-body"></div>
+
+<div class="modal-footer">
+    <button id="prev_step" type="button" class="btn btn-mini btn-info">Previous</button>
+    <button id="next_step" type="button" class="btn btn-mini btn-info">Next</button>
+</div>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/preview.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/preview.html b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/preview.html
new file mode 100644
index 0000000..3b9642f
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/preview.html
@@ -0,0 +1,10 @@
+<!-- New application wizard step 3: summary and submit the app -->
+<div>
+    <h3>Application Preview</h3>
+    <textarea id="app-summary" readonly="readonly" rows="16" style="width:100%;"></textarea>
+</div>
+<dl class="dl-horizontal"></dl>
+<div class="info-message hide label-message">
+        <span class="label-important">ERROR</span>
+        Invalid spec or server failure
+</div>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/home/modal-wizard.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/home/modal-wizard.html b/usage/jsgui/src/main/webapp/assets/tpl/home/modal-wizard.html
deleted file mode 100644
index 540b37a..0000000
--- a/usage/jsgui/src/main/webapp/assets/tpl/home/modal-wizard.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<div class="modal-header">
-    <button type="button" class="close" data-dismiss="modal">x</button>
-    <h3 id="step_title">Title placeholder</h3>
-
-    <p id="step_instructions">Instructions placeholder</p>
-</div>
-
-<div class="modal-body"></div>
-
-<div class="modal-footer">
-    <button id="prev_step" type="button" class="btn btn-mini btn-info">Previous</button>
-    <button id="next_step" type="button" class="btn btn-mini btn-info">Next</button>
-</div>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/home/step1-location-option.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/home/step1-location-option.html b/usage/jsgui/src/main/webapp/assets/tpl/home/step1-location-option.html
deleted file mode 100644
index 9242353..0000000
--- a/usage/jsgui/src/main/webapp/assets/tpl/home/step1-location-option.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<option value="<%= url %>">
-    <span class="provider"><%= name %></span>
-</option>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/home/step1-location-row.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/home/step1-location-row.html b/usage/jsgui/src/main/webapp/assets/tpl/home/step1-location-row.html
deleted file mode 100644
index ed51a2a..0000000
--- a/usage/jsgui/src/main/webapp/assets/tpl/home/step1-location-row.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<div id="location-row-<%= rowId %>" rowId="<%= rowId %>" initialValue="<%= initialValue %>" class="location-selector-row">
-    <select id="select-location" style="margin:4px 0 4px 0; width:80%"></select>
-    <% if (rowId>0) { %>
-        <button id="remove-app-location" class="btn btn-info btn-mini" type="button"><i class="icon-minus-sign"></i></button>
-    <% } %>    
-</div>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/home/step1.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/home/step1.html b/usage/jsgui/src/main/webapp/assets/tpl/home/step1.html
deleted file mode 100644
index b69f5ba..0000000
--- a/usage/jsgui/src/main/webapp/assets/tpl/home/step1.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!-- New application wizard step 1: set name and locations -->
-
-<div class="info-message label-message hide">
-        <span class="label-important">Important</span>
-        Name and location must be specified
-</div>
-<div class="info-nolocs-message label-message hide">
-        <span class="label-important">Important</span> 
-        Missing or unconfigured locations
-</div>
-
-
-<div class="control-group">
-    <label for="application-name">Name</label>
-
-    <div class="controls">
-        <input id="application-name" name="name" type="text" style="width:80%">
-    </div>
-</div>
-
-<div id="app-locations">
-    <div>Locations</div>
-    <div id="selector-container" class="control-group">
-    </div>
-    <button id="add-selector-container" class="btn btn-info btn-mini">Add Additional Location</button>
-</div>
-
-</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/home/step2-config-entry.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/home/step2-config-entry.html b/usage/jsgui/src/main/webapp/assets/tpl/home/step2-config-entry.html
deleted file mode 100644
index 8bf1df1..0000000
--- a/usage/jsgui/src/main/webapp/assets/tpl/home/step2-config-entry.html
+++ /dev/null
@@ -1,6 +0,0 @@
-			<div class="controls step2-entity-config">
-				<input id="key" type="text" class="input-medium" name="key" placeholder="key"> 
-				<input id="value" type="text" class="input-medium" name="value" placeholder="value">
-				<button id="remove-config" class="btn btn-info btn-mini" type="button">
-					<i class="icon-minus-sign"></i> </button>
-			</div>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/home/step2-entity-entry.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/home/step2-entity-entry.html b/usage/jsgui/src/main/webapp/assets/tpl/home/step2-entity-entry.html
deleted file mode 100644
index 1c49cc3..0000000
--- a/usage/jsgui/src/main/webapp/assets/tpl/home/step2-entity-entry.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<div class="editable-entity-group">
-	<div class="editable-entity-heading hide">
-        <span id="entity-name-header"><i>(new entity)</i></span>
-        <div style="float: right;">
-            <button class="btn btn-info btn-mini"><i class="icon-chevron-down"></i></button>
-        </div>
-	</div>
-	
-	<div class="editable-entity-body">
-        <div class="entity-info-message label-message hide">
-            <span class="label-important">ERROR</span>  Invalid entity/type definition
-        </div>
-        
-        <div style="float: right;">
-            <button class="btn btn-info btn-mini remove-entity-button">
-                <i class="icon-remove"></i> </button>
-            <button class="btn btn-info btn-mini editable-entity-button">
-                <i class="icon-chevron-up"></i> </button>
-        </div>
-		<div class="control-group">
-			<div class="controls">
-			    <div class="step2-entity-label">Name</div>
-				<input id="entity-name" type="text" class="input-large step2-entity-input" name="name" placeholder="name">
-			</div>
-		</div>
-
-		<div class="control-group">
-			<div class="controls">
-			    <div class="step2-entity-label">Type</div>
-				<input id="entity-type" type="text" name="type" class="input-large step2-entity-input entity-type-input" placeholder="type">
-			</div>
-		</div>
-
-		<div class="control-group">
-			<div class="step2-entity-label-newline">Configuration</div>
-            <div class="controls">
-            </div>
-			<div>
-				<button id="add-config" class="btn btn-mini btn-info">
-				    Add Config Key</button>
-			</div>
-		</div>
-	</div>
-</div>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/home/step2.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/home/step2.html b/usage/jsgui/src/main/webapp/assets/tpl/home/step2.html
deleted file mode 100644
index fb65297..0000000
--- a/usage/jsgui/src/main/webapp/assets/tpl/home/step2.html
+++ /dev/null
@@ -1,64 +0,0 @@
-<!-- New application wizard step 2: add and configure entities -->
-
-<div class="step2-info-message label-message hide">
-	<div class="label-important">ERROR</div>  Invalid application type/spec
-</div>
-
-<div id="step2body">
-
-	<ul class="nav nav-tabs" id="step2Tab">
-		<li class="active"><a href="#templateTab" data-toggle="tab">Template</a></li>
-		<li><a href="#entitiesTab" data-toggle="tab">Entities</a></li>
-		
-		<li class="dropdown"><a class="dropdown-toggle" id="dropLanguage"
-			role="button" data-toggle="dropdown" href="#">Code <b class="caret"></b></a>
-			<ul id="menuLanguage" class="dropdown-menu" role="menu" aria-labelledby="dropLanguage">
-				<li><a tabindex="-1" data-toggle="tab" href="#jsonTab">JSON</a></li>
-				<li><a tabindex="-1" data-toggle="tab" href="#xmlTab">XML</a></li>
-				<li class="divider"></li>
-				<li><a tabindex="-1" data-toggle="tab" href="#groovyTab">Groovy</a></li>
-				<li class="divider"></li>
-				<li><a tabindex="-1" data-toggle="tab" href="#uploadTab">JAR</a></li>
-			</ul></li>
-	</ul>
-
-  <div class="tab-content">
-  <div class="tab-pane active" id="templateTab">
-  
-        <div class="control-group">
-            <div class="step2-entity-label-newline">Type</div>
-            <div class="controls app-type">
-                <input id="entity-type" type="text" name="type" class="input-large step2-entity-input application-type-input" placeholder="type">
-            </div>
-        </div>
-
-        <div class="control-group">
-            <div class="step2-entity-label-newline">Configuration</div>
-            <div class="controls">
-            </div>
-            <div>
-                <button id="add-config" class="btn btn-mini btn-info">
-                    Add Config Key</button>
-            </div>
-        </div>
-  </div>
-
-  
-  <div class="tab-pane" id="entitiesTab">
-    <div id="entitiesAccordionish"></div>
-    <button id="add-app-entity" class="btn btn-info btn-mini" style="margin:5px;">Add Additional Entity</button>
-  </div>
-
-  <div class="tab-pane" id="jsonTab">
-    <br/><br/><i>coming soon!</i>
-  </div>
-  <div class="tab-pane" id="xmlTab">
-    <br/><br/><i>coming soon!</i>
-  </div>
-  <div class="tab-pane" id="groovyTab">
-    <br/><br/><i>coming soon!</i>
-  </div>
-  <div class="tab-pane" id="uploadTab">
-    <br/><br/><i>coming soon!</i>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/main/webapp/assets/tpl/home/step3.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/home/step3.html b/usage/jsgui/src/main/webapp/assets/tpl/home/step3.html
deleted file mode 100644
index 1407cf7..0000000
--- a/usage/jsgui/src/main/webapp/assets/tpl/home/step3.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!-- New application wizard step 3: summary and submit the app -->
-<div>
-    <h3>Request preview:</h3>
-    <textarea id="app-summary" readonly="readonly" rows="16" style="width:100%;"></textarea>
-</div>
-<dl class="dl-horizontal"></dl>
-<div class="info-message hide label-message">
-        <span class="label-important">ERROR</span>
-        Invalid JSON spec or server failure
-</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/test/javascript/specs/view/application-add-wizard-spec.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/test/javascript/specs/view/application-add-wizard-spec.js b/usage/jsgui/src/test/javascript/specs/view/application-add-wizard-spec.js
new file mode 100644
index 0000000..adae3d9
--- /dev/null
+++ b/usage/jsgui/src/test/javascript/specs/view/application-add-wizard-spec.js
@@ -0,0 +1,194 @@
+/**
+ * Test the ModalWizard can build a modal to view, edit and submit an application.
+ */
+define([
+    "underscore", "jquery", "backbone", "view/application-add-wizard", "model/application", "model/location",
+    "text!tpl/home/step1.html", "text!tpl/home/step2.html", "text!tpl/home/step3.html",
+    "text!tpl/home/step1-location-row.html", "text!tpl/home/step1-location-option.html",
+    "text!tpl/home/step2-entity-entry.html", "text!tpl/home/step2-config-entry.html"
+], function (_, $, Backbone, AppAddWizard, Application, Locations, Entities,
+		Step1Html, Step2Html, Step3Html,
+		Step1LocationRowHtml, LocationOptionHtml,  
+		Step2EntityEntryHtml, Step2ConfigEntryHtml) {
+
+	/* TEST disabled until we can more cleanly supply javascript.
+	 * should probably move to have one big model,
+	 * rather than passing around lots of small model items.
+	 */
+	
+//    Backbone.View.prototype.close = function () {
+//        if (this.beforeClose) {
+//            this.beforeClose()
+//        }
+//        this.remove()
+//        this.unbind()
+//    }
+//
+//    var fakeRouter = new Backbone.Router()
+//    var modal = new ModalWizard({appRouter:fakeRouter});
+//
+//    describe("view/modal-wizard", function () {
+//
+//        it("creates an empty Application.Spec", function () {
+//            expect(modal.model.get("name")).toBe("")
+//            expect(modal.model.get("entities")).toEqual([])
+//            expect(modal.model.get("locations")).toEqual([])
+//        })
+//
+//        it("creates a view for each of the 3 steps", function () {
+//            expect(modal.steps.length).toBe(3)
+//            expect(modal.steps[0].view instanceof ModalWizard.Step1).toBeTruthy()
+//            expect(modal.steps[1].view instanceof ModalWizard.Step2).toBeTruthy()
+//            expect(modal.steps[2].view instanceof ModalWizard.Step3).toBeTruthy()
+//        })
+//
+//        it("beforeClose method closes all 3 subviews", function () {
+//            spyOn(Backbone.View.prototype, "close").andCallThrough()
+//            modal.beforeClose()
+//            expect(modal.steps[0].view.close).toHaveBeenCalled()
+//            expect(modal.steps[1].view.close).toHaveBeenCalled()
+//            expect(modal.steps[2].view.close).toHaveBeenCalled()
+//        })
+//    })
+//
+//    describe("view/modal-wizard step1", function () {
+//        var app, view
+//
+//        beforeEach(function () {
+//            app = new Application.Spec
+//            view = new ModalWizard.Step1({model:app})
+//            view.locations.url = "fixtures/location-list.json"
+//            view.locations.fetch({async:false})
+//            view.render()
+//        })
+//
+//        afterEach(function () {
+//            view.close()
+//        })
+//
+//        it("does not validate empty view", function () {
+//            expect(view.validate()).toBe(false)
+//            expect(view.$("#app-locations ul").html()).toBe("")
+//            expect(view.$("#application-name").text()).toBe("")
+//        })
+//
+//        it("updates the name on blur", function () {
+//            view.$("#application-name").val("myapp")
+//            view.$("#application-name").trigger("blur")
+//            expect(app.get("name")).toBe("myapp")
+//        })
+//
+//        it("adds and removes location", function () {
+//            expect(app.get("locations").length).toBe(0)
+//            view.$("#add-app-location").trigger("click")
+//            expect(app.get("locations").length).toBe(1)
+//            view.$(".remove").trigger("click")
+//            expect(app.get("locations").length).toBe(0)
+//        })
+//
+//    })
+//
+//    describe("view/modal-wizard step2", function () {
+//        var app, view
+//
+//        beforeEach(function () {
+//            app = new Application.Spec
+//            view = new ModalWizard.Step2({model:app})
+//            // TODO supply catalog entities
+//            view.render()
+//        })
+//
+//        afterEach(function () {
+//            view.close()
+//        })
+//
+//        // to be added
+//    })
+//
+//    describe("view/modal-wizard step3", function () {
+//        var app, view
+//
+//        beforeEach(function () {
+//            app = new Application.Spec
+//            view = new ModalWizard.Step3({model:app})
+//            view.render()
+//        })
+//
+//        afterEach(function () {
+//            view.close()
+//        })
+//
+//        it("has #app-summary to render the application", function () {
+//            expect(view.$("#app-summary").length).toBe(1)
+//        })
+//
+//        it("validates only when application spec contains data", function () {
+//            expect(view.validate()).toBe(false)
+//            app.set({name:"myapp", locations:["/dummy/1"], entities:[
+//                {}
+//            ]})
+//            expect(view.validate()).toBe(true)
+//        })
+//    })
+//
+//    describe('tpl/home/step1.html', function () {
+//        var $step = $(Step1Html)
+//
+//        it('must have input#application-name', function () {
+//            expect($step.find('input#application-name').length).toEqual(1);
+//        });
+//
+//        it('must have div#app-locations', function () {
+//            var $appLocations = $step.filter('div#app-locations');
+//            expect($appLocations.length).toEqual(1);
+//            expect($appLocations.find('h4').length).toEqual(1);
+//            expect($appLocations.find('ul').length).toEqual(1);
+//            expect($appLocations.find('button#toggle-selector-container').length).toEqual(1);
+//        });
+//
+//        it('must have select#select-location', function () {
+//            expect($step.find('select#select-location').length).toEqual(1);
+//        });
+//
+//        it('must have button#add-app-location', function () {
+//            expect($step.find('button#add-app-location').length).toEqual(1);
+//        });
+//        it('must have div.info-message', function () {
+//            expect($step.filter('div.info-message').length).toEqual(1);
+//        })
+//    });
+//
+//    describe('tpl/home/step2.html', function () {
+//        var $step = $(Step2Html);
+//
+//        it('must have div#entities with h4 and ul', function () {
+//            var $div = $step.filter('div#entities');
+//            expect($div.length).toEqual(1);
+//            expect($div.find('h4').length).toEqual(1);
+//            expect($div.find('ul').length).toEqual(1);
+//            expect($div.find('button#toggle-entity-form').length).toEqual(1);
+//        });
+//
+//        it('must have div#new-entity with all components', function () {
+//            var $div = $step.filter('div#new-entity');
+//            expect($div.length).toEqual(1);
+//            expect($div.find('div#entity-form').length).toEqual(1);
+//            expect($div.find('button#add-app-entity').length).toEqual(1);
+//            expect($div.find('div.entity-info-message').length).toEqual(1);
+//        });
+//        it('must have div.info-message', function () {
+//            expect($step.filter('div.info-message').length).toEqual(1);
+//        });
+//    });
+//
+//    describe('tpl/home/step3.html', function () {
+//        var $step = $(Step3Html);
+//        it('div.body must have h3 and textarea#app-summary', function () {
+//            expect($step.find('h3').length).toEqual(1);
+//            expect($step.find('textarea#app-summary').length).toEqual(1);
+//        });
+//        it('must have div.info-message', function () {
+//            expect($step.filter('div.info-message').length).toEqual(1);
+//        });
+//    });
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b0412050/usage/jsgui/src/test/javascript/specs/view/modal-wizard-spec.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/test/javascript/specs/view/modal-wizard-spec.js b/usage/jsgui/src/test/javascript/specs/view/modal-wizard-spec.js
deleted file mode 100644
index f98d70e..0000000
--- a/usage/jsgui/src/test/javascript/specs/view/modal-wizard-spec.js
+++ /dev/null
@@ -1,194 +0,0 @@
-/**
- * Test the ModalWizard can build a modal to view, edit and submit an application.
- */
-define([
-    "underscore", "jquery", "backbone", "view/modal-wizard", "model/application", "model/location",
-    "text!tpl/home/step1.html", "text!tpl/home/step2.html", "text!tpl/home/step3.html",
-    "text!tpl/home/step1-location-row.html", "text!tpl/home/step1-location-option.html",
-    "text!tpl/home/step2-entity-entry.html", "text!tpl/home/step2-config-entry.html"
-], function (_, $, Backbone, ModalWizard, Application, Locations, Entities,
-		Step1Html, Step2Html, Step3Html,
-		Step1LocationRowHtml, LocationOptionHtml,  
-		Step2EntityEntryHtml, Step2ConfigEntryHtml) {
-
-	/* TEST disabled until we can more cleanly supply javascript.
-	 * should probably move to have one big model,
-	 * rather than passing around lots of small model items.
-	 */
-	
-//    Backbone.View.prototype.close = function () {
-//        if (this.beforeClose) {
-//            this.beforeClose()
-//        }
-//        this.remove()
-//        this.unbind()
-//    }
-//
-//    var fakeRouter = new Backbone.Router()
-//    var modal = new ModalWizard({appRouter:fakeRouter});
-//
-//    describe("view/modal-wizard", function () {
-//
-//        it("creates an empty Application.Spec", function () {
-//            expect(modal.model.get("name")).toBe("")
-//            expect(modal.model.get("entities")).toEqual([])
-//            expect(modal.model.get("locations")).toEqual([])
-//        })
-//
-//        it("creates a view for each of the 3 steps", function () {
-//            expect(modal.steps.length).toBe(3)
-//            expect(modal.steps[0].view instanceof ModalWizard.Step1).toBeTruthy()
-//            expect(modal.steps[1].view instanceof ModalWizard.Step2).toBeTruthy()
-//            expect(modal.steps[2].view instanceof ModalWizard.Step3).toBeTruthy()
-//        })
-//
-//        it("beforeClose method closes all 3 subviews", function () {
-//            spyOn(Backbone.View.prototype, "close").andCallThrough()
-//            modal.beforeClose()
-//            expect(modal.steps[0].view.close).toHaveBeenCalled()
-//            expect(modal.steps[1].view.close).toHaveBeenCalled()
-//            expect(modal.steps[2].view.close).toHaveBeenCalled()
-//        })
-//    })
-//
-//    describe("view/modal-wizard step1", function () {
-//        var app, view
-//
-//        beforeEach(function () {
-//            app = new Application.Spec
-//            view = new ModalWizard.Step1({model:app})
-//            view.locations.url = "fixtures/location-list.json"
-//            view.locations.fetch({async:false})
-//            view.render()
-//        })
-//
-//        afterEach(function () {
-//            view.close()
-//        })
-//
-//        it("does not validate empty view", function () {
-//            expect(view.validate()).toBe(false)
-//            expect(view.$("#app-locations ul").html()).toBe("")
-//            expect(view.$("#application-name").text()).toBe("")
-//        })
-//
-//        it("updates the name on blur", function () {
-//            view.$("#application-name").val("myapp")
-//            view.$("#application-name").trigger("blur")
-//            expect(app.get("name")).toBe("myapp")
-//        })
-//
-//        it("adds and removes location", function () {
-//            expect(app.get("locations").length).toBe(0)
-//            view.$("#add-app-location").trigger("click")
-//            expect(app.get("locations").length).toBe(1)
-//            view.$(".remove").trigger("click")
-//            expect(app.get("locations").length).toBe(0)
-//        })
-//
-//    })
-//
-//    describe("view/modal-wizard step2", function () {
-//        var app, view
-//
-//        beforeEach(function () {
-//            app = new Application.Spec
-//            view = new ModalWizard.Step2({model:app})
-//            // TODO supply catalog entities
-//            view.render()
-//        })
-//
-//        afterEach(function () {
-//            view.close()
-//        })
-//
-//        // to be added
-//    })
-//
-//    describe("view/modal-wizard step3", function () {
-//        var app, view
-//
-//        beforeEach(function () {
-//            app = new Application.Spec
-//            view = new ModalWizard.Step3({model:app})
-//            view.render()
-//        })
-//
-//        afterEach(function () {
-//            view.close()
-//        })
-//
-//        it("has #app-summary to render the application", function () {
-//            expect(view.$("#app-summary").length).toBe(1)
-//        })
-//
-//        it("validates only when application spec contains data", function () {
-//            expect(view.validate()).toBe(false)
-//            app.set({name:"myapp", locations:["/dummy/1"], entities:[
-//                {}
-//            ]})
-//            expect(view.validate()).toBe(true)
-//        })
-//    })
-//
-//    describe('tpl/home/step1.html', function () {
-//        var $step = $(Step1Html)
-//
-//        it('must have input#application-name', function () {
-//            expect($step.find('input#application-name').length).toEqual(1);
-//        });
-//
-//        it('must have div#app-locations', function () {
-//            var $appLocations = $step.filter('div#app-locations');
-//            expect($appLocations.length).toEqual(1);
-//            expect($appLocations.find('h4').length).toEqual(1);
-//            expect($appLocations.find('ul').length).toEqual(1);
-//            expect($appLocations.find('button#toggle-selector-container').length).toEqual(1);
-//        });
-//
-//        it('must have select#select-location', function () {
-//            expect($step.find('select#select-location').length).toEqual(1);
-//        });
-//
-//        it('must have button#add-app-location', function () {
-//            expect($step.find('button#add-app-location').length).toEqual(1);
-//        });
-//        it('must have div.info-message', function () {
-//            expect($step.filter('div.info-message').length).toEqual(1);
-//        })
-//    });
-//
-//    describe('tpl/home/step2.html', function () {
-//        var $step = $(Step2Html);
-//
-//        it('must have div#entities with h4 and ul', function () {
-//            var $div = $step.filter('div#entities');
-//            expect($div.length).toEqual(1);
-//            expect($div.find('h4').length).toEqual(1);
-//            expect($div.find('ul').length).toEqual(1);
-//            expect($div.find('button#toggle-entity-form').length).toEqual(1);
-//        });
-//
-//        it('must have div#new-entity with all components', function () {
-//            var $div = $step.filter('div#new-entity');
-//            expect($div.length).toEqual(1);
-//            expect($div.find('div#entity-form').length).toEqual(1);
-//            expect($div.find('button#add-app-entity').length).toEqual(1);
-//            expect($div.find('div.entity-info-message').length).toEqual(1);
-//        });
-//        it('must have div.info-message', function () {
-//            expect($step.filter('div.info-message').length).toEqual(1);
-//        });
-//    });
-//
-//    describe('tpl/home/step3.html', function () {
-//        var $step = $(Step3Html);
-//        it('div.body must have h3 and textarea#app-summary', function () {
-//            expect($step.find('h3').length).toEqual(1);
-//            expect($step.find('textarea#app-summary').length).toEqual(1);
-//        });
-//        it('must have div.info-message', function () {
-//            expect($step.filter('div.info-message').length).toEqual(1);
-//        });
-//    });
-})
\ No newline at end of file