| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /** |
| * Supplies a modal (wizardy) with a list of apps a user can pick. |
| * Templates (items with yaml and with a location) go to composer, |
| * other items (java, or yaml without a location) go to next *or* yaml composer. |
| */ |
| |
| define([ |
| "underscore", "jquery", "backbone", "brooklyn-utils", "js-yaml", "codemirror", |
| "model/entity", "model/application", "model/location", "model/catalog-application", |
| "text!tpl/app-add-wizard/modal-wizard.html", |
| "text!tpl/app-add-wizard/create.html", |
| "text!tpl/app-add-wizard/create-step-template-entry.html", |
| "text!tpl/app-add-wizard/required-config-entry.html", |
| "text!tpl/app-add-wizard/edit-config-entry.html", |
| "text!tpl/app-add-wizard/deploy.html", |
| "text!tpl/app-add-wizard/deploy-version-option.html", |
| "text!tpl/app-add-wizard/deploy-location-row.html", |
| "text!tpl/app-add-wizard/deploy-location-option.html", |
| //below not part of constructor |
| "codemirror-mode-yaml", |
| "codemirror-addon-show-hint", |
| "codemirror-addon-anyword-hint", |
| "codemirror-addon-display-placeholder", |
| "bootstrap" |
| ], function (_, $, Backbone, Util, JsYaml, CodeMirror, Entity, Application, Location, CatalogApplication, |
| ModalHtml, CreateHtml, CreateStepTemplateEntryHtml, |
| RequiredConfigEntryHtml, EditConfigEntryHtml, DeployHtml, |
| DeployVersionOptionHtml, DeployLocationRowHtml, DeployLocationOptionHtml |
| ) { |
| /** Special ID to indicate that no locations will be provided when starting the server. */ |
| var NO_LOCATION_INDICATOR = "__NONE__"; |
| |
| function setVisibility(obj, isVisible) { |
| if (isVisible) obj.show(); |
| else obj.hide(); |
| } |
| |
| function setEnablement(obj, isEnabled) { |
| obj.attr("disabled", !isEnabled) |
| } |
| |
| /** converts old-style spec with "entities" to camp-style spec with services */ |
| function oldSpecToCamp(spec) { |
| var services; |
| if (spec.type) { |
| services = [entityToCamp({type: spec.type, version: spec.version, config: spec.config})]; |
| } else if (spec.entities) { |
| services = []; |
| var entities = spec.entities; |
| for (var i = 0; i < entities.length; i++) { |
| services.push(entityToCamp(entities[i])); |
| } |
| } |
| var result = {}; |
| if (spec.name) result.name = spec.name; |
| if (spec.locations) { |
| if (spec.locations.length>1) |
| result.locations = spec.locations; |
| else |
| result.location = spec.locations[0]; |
| } |
| if (services) result.services = services; |
| // NB: currently nothing else is supported in this spec |
| return result; |
| } |
| function entityToCamp(entity) { |
| var result = {}; |
| if (entity.name && (!options || !options.exclude_name)) result.name = entity.name; |
| if (entity.type) result.type = entity.type; |
| if (entity.type && entity.version) result.type += ":" + entity.version; |
| if (entity.config && _.size(entity.config)) result["brooklyn.config"] = entity.config; |
| return result; |
| } |
| function getConvertedConfigValue(value) { |
| try { |
| return $.parseJSON(value); |
| } catch (e) { |
| return value; |
| } |
| } |
| |
| function redirectToEditorTabToDeployId(catalogId){ |
| redirectTo("/v1/editor/app/"+ (typeof catalogId === 'string' ? catalogId : '')); |
| } |
| function redirectToEditorTabToDeployYaml(yaml){ |
| redirectTo("/v1/editor/app/_/"+encodeURIComponent(yaml)); |
| } |
| function redirectTo(url){ |
| var $modal = $('.add-app #modal-container .modal'); |
| $modal.modal('hide'); |
| $modal.fadeTo(500,1); |
| Backbone.history.navigate(url, {trigger: true}); |
| } |
| |
| |
| var ModalWizard = Backbone.View.extend({ |
| tagName:'div', |
| className:'modal hide fade', |
| events:{ |
| 'click #prev_step':'prevStep', |
| 'click #next_step':'nextStep', |
| 'click #preview_step':'previewStep', |
| 'click #finish_step':'finishStep' |
| }, |
| template:_.template(ModalHtml), |
| initialize:function () { |
| this.catalog = {} |
| this.catalog.applications = {} |
| this.model = {} |
| this.model.spec = new Application.Spec; |
| this.model.yaml = ""; |
| this.currentStep = 0; |
| this.steps = [ |
| { |
| step_id:'what-app', |
| title:'Create Application', |
| instructions:'Choose or build the application to deploy', |
| view:new ModalWizard.StepCreate({ model:this.model, wizard: this, catalog: this.catalog }) |
| }, |
| { |
| // TODO rather than make this another step -- since we now on preview revert to the yaml tab |
| // this should probably be shown in the catalog tab, replacing the other contents. |
| step_id:'name-and-locations', |
| title:'<%= appName %>', |
| instructions:'Specify the locations to deploy to and any additional configuration', |
| view:new ModalWizard.StepDeploy({ model:this.model, catalog: this.catalog }) |
| } |
| ] |
| }, |
| 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 (callback) { |
| var name = this.model.name || ""; |
| this.title = this.$("h3#step_title") |
| this.instructions = this.$("p#step_instructions") |
| |
| var currentStepObj = this.steps[this.currentStep] |
| this.title.html(_.template(currentStepObj.title)({appName: name})); |
| this.instructions.html(_.escape(currentStepObj.instructions)) |
| this.currentView = currentStepObj.view |
| |
| // delegate to sub-views !! |
| this.currentView.render() |
| this.currentView.updateForState() |
| this.$(".modal-body").replaceWith(this.currentView.el) |
| if (callback) callback(this.currentView); |
| |
| this.updateButtonVisibility(); |
| }, |
| updateButtonVisibility:function () { |
| var currentStepObj = this.steps[this.currentStep] |
| |
| setVisibility(this.$("#prev_step"), (this.currentStep > 0)) |
| |
| var isTemplateWithLocation = false; |
| { |
| var yaml = (this.currentView && this.currentView.selectedTemplate && this.currentView.selectedTemplate.yaml); |
| if (yaml) { |
| try { |
| yaml = JsYaml.safeLoad(yaml); |
| hasLocation = yaml.location || yaml.locations; |
| if (!hasLocation) { |
| // look for locations defined in locations |
| svcs = yaml.services; |
| if (svcs) { |
| for (svcI in svcs) { |
| if (svcs[svcI].location || svcs[svcI].locations) { |
| hasLocation = true; |
| break; |
| } |
| } |
| } |
| } |
| isTemplateWithLocation = (hasLocation ? true : false); |
| } catch (e) { |
| log("Warning: could not parse yaml template of selected item") |
| log(yaml); |
| } |
| } |
| } |
| |
| // preview (aka "YAML Composer") enabled and shown always |
| setVisibility(this.$("#preview_step"), true); |
| setEnablement(this.$("#preview_step"), true) |
| |
| // next shown for first step, but not for templates |
| var nextVisible = (this.currentStep < 1) && (!this.isTemplate); |
| setVisibility(this.$("#next_step"), nextVisible); |
| |
| // now set next/preview enablement |
| var nextEnabled = true; |
| if (this.currentStep==0 && currentStepObj && currentStepObj.view) { |
| // disable if nothing is selected in template (app lozenge list) view |
| // or if it is a template with a location |
| if (!currentStepObj.view.selectedTemplate || isTemplateWithLocation) |
| nextEnabled = false; |
| } |
| |
| if (nextVisible) setEnablement(this.$("#next_step"), nextEnabled) |
| |
| // finish ("deploy") is shown on the config step |
| var finishVisible = (this.currentStep >= 1); |
| var finishEnabled = finishVisible; |
| setVisibility(this.$("#finish_step"), finishVisible) |
| setEnablement(this.$("#finish_step"), finishEnabled) |
| }, |
| |
| submitApplication:function (event) { |
| var that = this |
| var $modal = $('.add-app #modal-container .modal') |
| $modal.fadeTo(500,0.5); |
| |
| var yaml; |
| // Drop any "None" locations. |
| this.model.spec.pruneLocations(); |
| yaml = JsYaml.safeDump(oldSpecToCamp(this.model.spec.toJSON())); |
| |
| $.ajax({ |
| url:'/v1/applications', |
| type:'post', |
| contentType:'application/yaml', |
| processData:false, |
| data:yaml, |
| success:function (data) { |
| that.onSubmissionComplete(true, data, $modal) |
| }, |
| error:function (data) { |
| that.onSubmissionComplete(false, data, $modal) |
| } |
| }); |
| |
| return false |
| }, |
| onSubmissionComplete: function(succeeded, data, $modal) { |
| var that = this; |
| if (succeeded) { |
| $modal.modal('hide') |
| $modal.fadeTo(500,1); |
| if (that.options.callback) that.options.callback(); |
| } else { |
| log("ERROR submitting application: "+data.responseText); |
| var response, summary="Server responded with an error"; |
| try { |
| if (data.responseText) { |
| response = JSON.parse(data.responseText) |
| if (response) { |
| summary = response.message; |
| } |
| } |
| } catch (e) { |
| summary = data.responseText; |
| } |
| that.$el.fadeTo(100,1).delay(200).fadeTo(200,0.2).delay(200).fadeTo(200,1); |
| that.steps[that.currentStep].view.showFailure(summary) |
| } |
| }, |
| |
| prevStep:function () { |
| this.currentStep -= 1; |
| this.renderCurrentStep(); |
| }, |
| nextStep:function () { |
| if (this.currentStep == 0) { |
| if (this.currentView.validate()) { |
| // next not visible if it's a template, so go to create (configure) step |
| this.currentStep += 1; |
| this.renderCurrentStep(); |
| } else { |
| // the call to validate will have done the showFailure |
| } |
| } else { |
| throw "Unexpected step: "+this.currentStep; |
| } |
| }, |
| previewStep:function () { |
| // no need for validation if going to composer |
| |
| // When user jump to the YAML editor he also must have the config values that he set through the UI. |
| // The config is set implicitly when user clicks deploy and StepDeploy.validate is called. |
| this.currentView.model.spec.set("config", this.currentView.getConfigMap()); |
| if (this.currentStep==0) { |
| // from first step, composer should load yaml from the catalog item |
| if (this.currentView.selectedTemplate && this.currentView.selectedTemplate.id) { |
| redirectToEditorTabToDeployId(this.currentView.selectedTemplate.id); |
| } else { |
| redirectToEditorTabToDeployId(); |
| } |
| } else { |
| // on second step we generate yaml |
| // Drop any "None" locations. |
| this.model.spec.pruneLocations(); |
| var yaml = JsYaml.safeDump(oldSpecToCamp(this.model.spec.toJSON())); |
| |
| redirectToEditorTabToDeployYaml(yaml); |
| } |
| }, |
| finishStep:function () { |
| if (this.currentView.validate()) { |
| this.submitApplication() |
| } else { |
| // call to validate should showFailure |
| } |
| }, |
| }) |
| |
| // 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 #remove-config':'removeConfigRow', |
| 'click #add-config':'addConfigRow', |
| 'click .template-lozenge':'templateClick', |
| 'keyup .text-filter input':'applyFilter', |
| 'change .text-filter input':'applyFilter', |
| 'paste .text-filter input':'applyFilter', |
| 'shown a[data-toggle="tab"]':'onTabChange', |
| 'click #templateTab #catalog-add':'switchToCatalogAdd', |
| 'click #templateTab #catalog-yaml':'redirectToEditorTab' |
| }, |
| template:_.template(CreateHtml), |
| wizard: null, |
| initialize:function () { |
| var self = this |
| self.catalogEntityIds = [] |
| |
| this.$el.html(this.template({})) |
| |
| // TODO: Make into models, allow options to override, then pass in in test |
| // with overrridden url. Can then think about fixing tests in application-add-wizard-spec.js. |
| $.get('/v1/catalog/entities', {}, function (result) { |
| self.catalogEntityItems = result |
| self.catalogEntityIds = _.map(result, function(item) { return item.id }) |
| }); |
| |
| this.options.catalog.applications = new CatalogApplication.Collection(); |
| this.options.catalog.applications.fetch({ |
| data: $.param({ |
| allVersions: true |
| }), |
| success: function (collection, response, options) { |
| $('#catalog-applications-throbber').hide(); |
| $('#catalog-applications-empty').hide(); |
| if (collection.size() > 0) { |
| self.addTemplateLozenges(); |
| } else { |
| $('#catalog-applications-empty').show(); |
| } |
| } |
| }); |
| }, |
| renderConfiguredEntities:function () { |
| var $configuredEntities = this.$('#entitiesAccordionish').empty() |
| var that = this |
| if (this.model.spec.get("entities") && this.model.spec.get("entities").length > 0) { |
| _.each(this.model.spec.get("entities"), function (entity) { |
| that.addEntityHtml($configuredEntities, entity) |
| }) |
| } |
| }, |
| updateForState: function () {}, |
| render:function () { |
| this.renderConfiguredEntities(); |
| this.delegateEvents(); |
| return this; |
| }, |
| // TODO tabs no longer relevant |
| onTabChange: function(e) { |
| var tabText = $(e.target).text(); |
| if (tabText=="Catalog") { |
| $("li.text-filter").show() |
| } else { |
| $("li.text-filter").hide() |
| } |
| |
| if (this.options.wizard) |
| this.options.wizard.updateButtonVisibility(); |
| }, |
| switchToCatalogAdd: function() { |
| var $modal = $('.add-app #modal-container .modal') |
| $modal.modal('hide'); |
| window.location.href="#v1/catalog/new"; |
| }, |
| redirectToEditorTab: function () { |
| redirectToEditorTabToDeployId(); |
| }, |
| applyFilter: function(e) { |
| var filter = $(e.currentTarget).val().toLowerCase() |
| if (!filter) { |
| $(".template-lozenge").show() |
| } else { |
| _.each($(".template-lozenge"), function(it) { |
| var viz = $(it).text().toLowerCase().indexOf(filter)>=0 |
| if (viz) |
| $(it).show() |
| else |
| $(it).hide() |
| }) |
| } |
| }, |
| addTemplateLozenges: function(event) { |
| var that = this |
| _.each(this.options.catalog.applications.getDistinctApplications(), function(item) { |
| that.addTemplateLozenge(that, item[0]) |
| }) |
| }, |
| addTemplateLozenge: function(that, item) { |
| var $tempel = _.template(CreateStepTemplateEntryHtml, { |
| id: item.get('id'), |
| type: item.get('type'), |
| name: item.get('name') || item.get('id'), |
| description: item.get('description'), |
| planYaml: item.get('planYaml'), |
| iconUrl: item.get('iconUrl') |
| }) |
| $("#create-step-template-entries", that.$el).append($tempel) |
| }, |
| templateClick: function(event) { |
| var $tl = $(event.target).closest(".template-lozenge"); |
| var wasSelected = $tl.hasClass("selected") |
| $(".template-lozenge").removeClass("selected") |
| if (!wasSelected) { |
| $tl.addClass("selected") |
| this.selectedTemplate = { |
| id: $tl.attr('id'), |
| type: $tl.data('type'), |
| name: $tl.data("name"), |
| yaml: $tl.data("yaml"), |
| }; |
| } else { |
| this.selectedTemplate = null; |
| } |
| |
| if (this.options.wizard) |
| this.options.wizard.updateButtonVisibility(); |
| }, |
| getConfigMap:function (root) { |
| var map = {} |
| $('.app-add-wizard-config-entry',root).each( function (index,elt) { |
| var value = getConvertedConfigValue($('#value',elt).val()); |
| if (value !== null) { |
| map[$('#key',elt).val()] = value; |
| } |
| }) |
| return map; |
| }, |
| saveTemplate:function () { |
| if (!this.selectedTemplate) return false |
| var type = this.selectedTemplate.type; |
| if (!this.options.catalog.applications.hasType(type)) { |
| $('.entity-info-message').show('slow').delay(2000).hide('slow') |
| return false |
| } |
| |
| this.model.spec.set("type", type); |
| this.model.name = this.selectedTemplate.name; |
| this.model.catalogEntityData = "LOAD" |
| return true; |
| }, |
| saveAppClass:function () { |
| var that = this |
| var tab = $.find('#appClassTab') |
| var type = $(tab).find('#app-java-type').val() |
| if (!this.options.catalog.applications.hasType(type)) { |
| $('.entity-info-message').show('slow').delay(2000).hide('slow') |
| return false |
| } |
| this.model.spec.set("type", type); |
| return true; |
| }, |
| addEntityHtml:function (parent, entity) { |
| var $entity = _.template(CreateEntityEntryHtml, {}) |
| var that = this |
| parent.append($entity) |
| parent.children().last().find('.entity-type-input').typeahead({ source: that.catalogEntityIds }) |
| }, |
| |
| addConfigRow:function (event) { |
| var $row = _.template(EditConfigEntryHtml, {}) |
| $(event.currentTarget).parent().prev().append($row) |
| }, |
| removeConfigRow:function (event) { |
| $(event.currentTarget).parent().remove() |
| }, |
| |
| validate:function () { |
| var that = this; |
| delete this.model.spec.attributes["id"] |
| if (this.saveTemplate()) { |
| this.model.spec.set("entities", []); |
| return true |
| } |
| this.showFailure("Invalid application type/spec"); |
| return false |
| }, |
| |
| showFailure: function(text) { |
| if (!text) text = "Failure performing the specified action"; |
| this.$('div.error-message .error-message-text').html(_.escape(text)); |
| this.$('div.error-message').slideDown(250).delay(10000).slideUp(500); |
| } |
| |
| }) |
| |
| ModalWizard.StepDeploy = Backbone.View.extend({ |
| className:'modal-body', |
| |
| events:{ |
| 'click #add-selector-container':'addLocation', |
| 'click #remove-app-location':'removeLocation', |
| 'change .select-version': 'selectionVersion', |
| 'change .select-location': 'selectionLocation', |
| 'blur #application-name':'updateName', |
| 'click #remove-config':'removeConfigRow', |
| 'click #add-config':'addConfigRow' |
| }, |
| |
| template:_.template(DeployHtml), |
| versionOptionTemplate:_.template(DeployVersionOptionHtml), |
| locationRowTemplate:_.template(DeployLocationRowHtml), |
| locationOptionTemplate:_.template(DeployLocationOptionHtml), |
| |
| initialize:function () { |
| this.model.spec.on("change", this.render, this) |
| this.$el.html(this.template()) |
| this.locations = new Location.Collection() |
| }, |
| beforeClose:function () { |
| this.model.spec.off("change", this.render) |
| }, |
| renderName:function () { |
| this.$('#application-name').val(this.model.spec.get("name")) |
| }, |
| renderVersions: function() { |
| var optionTemplate = this.versionOptionTemplate |
| select = this.$('.select-version') |
| container = this.$('#app-versions') |
| defaultVersion = '0.0.0.SNAPSHOT'; |
| |
| select.empty(); |
| |
| var versions = this.options.catalog.applications.getVersions(this.model.spec.get('type')); |
| for (var vi = 0; vi < versions.length; vi++) { |
| var version = versions[vi]; |
| select.append(optionTemplate({ |
| version: version |
| })); |
| } |
| |
| if (versions.length === 1 && versions[0] === defaultVersion) { |
| this.model.spec.set('version', ''); |
| container.hide(); |
| } else { |
| this.model.spec.set('version', versions[0]); |
| container.show(); |
| } |
| }, |
| renderAddedLocations:function () { |
| // renders the locations added to the model |
| var rowTemplate = this.locationRowTemplate, |
| optionTemplate = this.locationOptionTemplate, |
| container = this.$("#selector-container-location"); |
| container.empty(); |
| for (var li = 0; li < this.model.spec.get("locations").length; li++) { |
| var chosenLocation = this.model.spec.get("locations")[li]; |
| container.append(rowTemplate({ |
| initialValue: chosenLocation, |
| rowId: li |
| })); |
| } |
| var $locationOptions = container.find('.select-location'); |
| var templated = this.locations.map(function(aLocation) { |
| return optionTemplate({ |
| id: aLocation.getIdentifierName() || aLocation.id || "", |
| name: aLocation.getPrettyName() |
| }); |
| }); |
| |
| // insert "none" location |
| $locationOptions.append(templated.join("")); |
| $locationOptions.each(function(i) { |
| var option = $($locationOptions[i]); |
| option.val(option.parent().attr('initialValue')); |
| // Only append dashes if there are any locations |
| if (option.find("option").length > 0) { |
| option.append("<option disabled>------</option>"); |
| } |
| option.append(optionTemplate({ |
| id: NO_LOCATION_INDICATOR, |
| name: "None" |
| })); |
| }); |
| }, |
| render:function () { |
| this.delegateEvents() |
| return this |
| }, |
| updateForState: function () { |
| var that = this |
| // clear any error message (we are being displayed fresh; if there are errors in the update, we'll show them in code below) |
| this.$('div.error-message').hide(); |
| this.renderName() |
| this.renderVersions() |
| this.locations.fetch({ |
| success:function () { |
| if (that.model.spec.get("locations").length==0) |
| that.addLocation() |
| else |
| that.renderAddedLocations() |
| }}) |
| |
| if (this.model.catalogEntityData==null) { |
| this.renderStaticConfig(null) |
| } else if (this.model.catalogEntityData=="LOAD") { |
| console.log("loading", this.model.spec.get("type")); |
| this.renderStaticConfig("LOADING") |
| $.get('/v1/catalog/entities/'+encodeURIComponent(this.model.spec.get("type")) + '/latest', {}, function (result) { |
| that.model.catalogEntityData = result |
| that.renderStaticConfig(that.model.catalogEntityData) |
| }) |
| } else { |
| this.renderStaticConfig(this.model.catalogEntityData) |
| } |
| }, |
| addLocation:function () { |
| if (this.locations.models.length>0) { |
| this.model.spec.addLocation(this.locations.models[0].getIdentifierName()) |
| } else { |
| // i.e. No location |
| this.model.spec.addLocation(undefined); |
| } |
| this.renderAddedLocations() |
| }, |
| removeLocation:function (event) { |
| var toBeRemoved = $(event.currentTarget).parent().attr('rowId') |
| this.model.spec.removeLocationIndex(toBeRemoved) |
| this.renderAddedLocations() |
| }, |
| addConfigRow:function (event) { |
| var $row = _.template(EditConfigEntryHtml, {}) |
| $(event.currentTarget).parent().prev().append($row) |
| }, |
| removeConfigRow:function (event) { |
| $(event.currentTarget).parent().parent().remove() |
| }, |
| renderStaticConfig:function (catalogEntryItem) { |
| this.$('.config-table').html('') |
| if (catalogEntryItem=="LOADING") { |
| this.$('.required-config-loading').show() |
| } else { |
| var configs = [] |
| this.$('.required-config-loading').hide() |
| if (catalogEntryItem!=null && catalogEntryItem.config!=null) { |
| var that = this |
| _.chain(catalogEntryItem.config).sortBy(function (cfg){ |
| return !isNaN(cfg.priority) ? cfg.priority : 99999; |
| }).each(function (cfg) { |
| if (cfg.pinned === true || (cfg.contraints.indexOf('required') > -1 && (cfg.defaultValue === undefined || cfg.defaultValue === ''))) { |
| var html = _.template(RequiredConfigEntryHtml, {data:cfg}); |
| that.$('.config-table').append(html); |
| } |
| }) |
| } |
| } |
| }, |
| getConfigMap:function() { |
| var map = {}; |
| $('.app-add-wizard-config-entry').each( function (index,elt) { |
| var value = $('#checkboxValue',elt).length ? $('#checkboxValue',elt).is(':checked') : |
| getConvertedConfigValue($('#value',elt).val()); |
| if (value !== null) { |
| map[$('#key',elt).val()] = value; |
| } |
| }) |
| return map; |
| }, |
| selectionVersion:function (event) { |
| this.model.spec.set("version", $(event.currentTarget).val()) |
| }, |
| selectionLocation:function (event) { |
| var loc_id = $(event.currentTarget).val(), |
| isNoneLocation = loc_id === NO_LOCATION_INDICATOR; |
| var locationValid = isNoneLocation || this.locations.find(function (candidate) { |
| return candidate.getIdentifierName()==loc_id; |
| }); |
| if (!locationValid) { |
| log("invalid location "+loc_id); |
| this.showFailure("Invalid location "+loc_id); |
| this.model.spec.set("locations",[]); |
| } else { |
| var index = $(event.currentTarget).parent().attr('rowId'); |
| this.model.spec.setLocationAtIndex(index, isNoneLocation ? undefined : loc_id); |
| } |
| }, |
| updateName:function () { |
| var name = this.$('#application-name').val(); |
| if (name) |
| this.model.spec.set("name", name); |
| else |
| this.model.spec.set("name", ""); |
| }, |
| validate:function () { |
| this.model.spec.set("config", this.getConfigMap()) |
| if (this.model.spec.get("locations").length !== 0) { |
| return true; |
| } else { |
| this.showFailure("A location is required"); |
| return false; |
| } |
| }, |
| showFailure: function(text) { |
| if (!text) text = "Failure performing the specified action"; |
| log("showing error: "+text); |
| this.$('div.error-message .error-message-text').html(_.escape(text)); |
| // flash the error, but make sure it goes away (we do not currently have any other logic for hiding this error message) |
| this.$('div.error-message').slideDown(250).delay(10000).slideUp(500); |
| } |
| }) |
| |
| return ModalWizard |
| }) |