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 <[email protected]>
Authored: Mon Nov 19 14:54:05 2012 +0000
Committer: Alex Heneveld <[email protected]>
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

Reply via email to