| // 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. |
| |
| (function($, cloudStack) { |
| /** |
| * Instance wizard |
| */ |
| cloudStack.uiCustom.instanceWizard = function(args) { |
| return function(listViewArgs) { |
| var context = listViewArgs.context; |
| |
| var instanceWizard = function(data) { |
| var $wizard = $('#template').find('div.instance-wizard').clone(); |
| var $progress = $wizard.find('div.progress ul li'); |
| var $steps = $wizard.find('div.steps').children().hide(); |
| var $diagramParts = $wizard.find('div.diagram').children().hide(); |
| var $form = $wizard.find('form'); |
| |
| $form.validate(); |
| |
| // Close instance wizard |
| var close = function() { |
| $wizard.dialog('destroy'); |
| $('div.overlay').fadeOut(function() { |
| $('div.overlay').remove(); |
| }); |
| }; |
| |
| // Save instance and close wizard |
| var completeAction = function() { |
| var data = cloudStack.serializeForm($form); |
| var $wizardLoading = $('<div>').addClass('loading-overlay').appendTo($wizard).css('z-index', '10000'); |
| |
| // Pass network IPs |
| data['my-network-ips'] = []; |
| $form.find('.my-networks .select .specify-ip input[type=text]').each(function() { |
| var $input = $(this); |
| |
| if (!$input.closest('.select').find('input[type=checkbox]').is(':checked')) return true; |
| |
| data['my-network-ips'].push( |
| $input.closest('.select').hasClass('advanced') ? |
| $input.val() : null |
| ); |
| }); |
| data['new-network-ip'] = $form.find('.new-network .select.advanced .specify-ip input[type=text]').val(); |
| |
| // Handle multi-disk service offerings |
| if ($form.find('.multi-disk-select-container').size()) { |
| data['disk-offerings-multi'] = []; |
| |
| var $diskGroups = $form.find('.disk-select-group'); |
| var $selectedDisks = $.grep($diskGroups, function (diskGroup) { |
| return $(diskGroup).find('input[type=checkbox]:checked').size(); |
| }); |
| |
| $selectedDisks.map(function (disk) { |
| data['disk-offerings-multi'].push( |
| $.extend($(disk).data('json-obj'), { |
| _diskOfferingId: $(disk).find('input[type=radio]:checked').val() |
| }) |
| ); |
| }); |
| } |
| |
| args.action({ |
| // Populate data |
| context: context, |
| data: data, |
| $wizard: $wizard, |
| response: { |
| success: function(args) { |
| var $listView = $('.list-view.instances'); |
| |
| if ($listView.size()) { |
| var $loading = $('.list-view.instances').listView('prependItem', { |
| data: [{ |
| name: data.displayname ? data.displayname : _l('label.new.vm'), |
| zonename: $wizard.find('select[name=zoneid] option').filter(function() { |
| return $(this).val() == data.zoneid; |
| }).html(), |
| state: 'Creating' |
| }], |
| actionFilter: function(args) { |
| return []; |
| } |
| }); |
| } |
| |
| listViewArgs.complete({ |
| _custom: args._custom, |
| messageArgs: cloudStack.serializeForm($form), |
| $item: $listView.size() ? $loading : $('<div>') |
| }); |
| |
| close(); |
| }, |
| error: function(message) { |
| $wizard.remove(); |
| $('div.overlay').remove(); |
| |
| if (message) { |
| cloudStack.dialog.notice({ |
| message: message |
| }); |
| } |
| } |
| } |
| }); |
| }; |
| |
| var makeSelects = function(name, data, fields, options, selectedObj, selectedObjNonEditable) { |
| var $selects = $('<div>'); |
| options = options ? options : {}; |
| |
| $(data).each(function() { |
| var item = this; |
| var id = item[fields.id]; |
| |
| var $select = $('<div>') |
| .addClass('select') |
| .append( |
| $('<input>') |
| .attr({ |
| type: (function(type) { |
| return type ? type : 'radio'; |
| })(options ? options.type : null), |
| name: name, |
| 'wizard-field': options['wizard-field'] |
| }) |
| .val(id) |
| .click(function() { |
| var $select = $(this).closest('.select'); |
| var isSingleSelect = $select.hasClass('single-select'); |
| var $radio = $select.find('input[type=radio]'); |
| var $newNetwork = $(this).closest('.content').find('.select.new-network'); |
| var $otherSelects = $select.siblings().filter(':visible'); |
| var isCheckbox = $(this).attr('type') == 'checkbox'; |
| |
| if (isCheckbox) { |
| if (isSingleSelect) { |
| $select.siblings('.single-select:visible').find('input[type=checkbox]') |
| .attr('checked', false); |
| |
| var $checkedOtherSelect = $otherSelects.filter(function() { |
| return $(this).not('.single-select') && |
| $(this).find('input[type=checkbox]:checked').size() && |
| $(this).find('input[type=radio]:checked').size(); |
| }); |
| |
| if (!$checkedOtherSelect.size() && |
| !$('.new-network:visible input[type=radio]:checked').size()) { |
| $(this).closest('.select').find('input[type=radio]').click(); |
| } |
| } |
| |
| if ((!$otherSelects.size()) && |
| $newNetwork.find('input[type=checkbox]').is(':unchecked')) { |
| // Set as default |
| $(this).closest('.select').find('input[type=radio]').click(); |
| } |
| } |
| |
| if ($radio.is(':checked') && !$(this).is(':checked')) { |
| if (!$radio.closest('.select').index()) { |
| return false; |
| } else { |
| $otherSelects.filter(':first') |
| .find('input[type=radio]').click(); |
| } |
| } |
| |
| return true; |
| }) |
| ) |
| .append( |
| $('<div>').addClass('select-desc') |
| .append($('<div>').addClass('name').html(_s(this[fields.name]))) |
| .append($('<div>').addClass('desc').html(_s(this[fields.desc]))) |
| ) |
| .data('json-obj', this); |
| |
| if (selectedObj != null && selectedObj.id == item.id) { |
| $select.find('input[type=checkbox]').attr('checked', 'checked'); |
| |
| if (selectedObjNonEditable) { |
| $select.find('input[type=checkbox]').attr('disabled', 'disabled'); |
| $select.find('input[type=checkbox]').clone().attr({ |
| type: 'hidden', |
| disabled: false |
| }).appendTo($selects); |
| } |
| } |
| |
| $selects.append($select); |
| |
| if (item._singleSelect) { |
| $select.addClass('single-select'); |
| } |
| |
| if (options.secondary) { |
| var $secondary = $('<div>').addClass('secondary-input').append( |
| $('<input>') |
| .attr({ |
| type: options.secondary.type, |
| name: options.secondary.name, |
| 'wizard-field': options.secondary['wizard-field'] |
| }) |
| .val(id) |
| .click(function() { |
| var $checkbox = $(this).closest('.select').find('input[type=checkbox]'); |
| |
| if (!$checkbox.is(':checked')) { |
| $checkbox.attr('checked', true); |
| } |
| |
| if ($(this).closest('.select-container').hasClass('single-select')) { |
| $(this).closest('.select').siblings().find('input[type=checkbox]') |
| .attr('checked', false); |
| } |
| |
| if ($select.hasClass('single-select')) { |
| $select.siblings('.single-select:visible').find('input[type=checkbox]') |
| .attr('checked', false); |
| } |
| }) |
| .after( |
| $('<div>').addClass('name').html(options.secondary.desc) |
| ) |
| ).appendTo($select); |
| } |
| }); |
| |
| cloudStack.evenOdd($selects, 'div.select', { |
| even: function($elem) { |
| $elem.addClass('even'); |
| }, |
| odd: function($elem) { |
| $elem.addClass('odd'); |
| } |
| }); |
| |
| return $selects.children(); |
| }; |
| |
| var dataProvider = function(step, providerArgs, callback) { |
| // Call appropriate data provider |
| args.steps[step - 1]($.extend(providerArgs, { |
| currentData: cloudStack.serializeForm($form), |
| initArgs: args, |
| context: context |
| })); |
| }; |
| |
| var dataGenerators = { |
| setup: function($step, formData) { |
| var originalValues = function(formData) { |
| $step.find('select').val( |
| formData.zoneid |
| ); |
| |
| $step.find('input[type=radio]').filter(function() { |
| return $(this).val() == formData['select-template']; |
| }).click(); |
| }; |
| |
| return { |
| response: { |
| success: function(args) { |
| // Zones |
| $(args.data.zones).each(function() { |
| $step.find('.select-zone select').append( |
| $('<option>') |
| .attr({ |
| value: this.id, |
| 'wizard-field': 'zone' |
| }) |
| .html(this.name) |
| ); |
| }); |
| |
| originalValues(formData); |
| } |
| } |
| }; |
| }, |
| |
| 'select-iso': function($step, formData) { |
| $step.find('.section.custom-size').hide(); |
| |
| var originalValues = function(formData) { |
| var $inputs = $step.find('.wizard-step-conditional:visible') |
| .find('input[type=radio]'); |
| var $selected = $inputs.filter(function() { |
| return $(this).val() == formData.templateid; |
| }); |
| |
| if (!$selected.size()) { |
| $inputs.filter(':first').click(); |
| } else { |
| $selected.click(); |
| } |
| $step.find('select[name=hypervisorid]:visible').val( |
| formData.hypervisorid |
| ); |
| }; |
| |
| $step.find('.wizard-step-conditional').hide(); |
| |
| return { |
| response: { |
| success: function(args) { |
| if (formData['select-template']) { |
| $step.find('.wizard-step-conditional').filter(function() { |
| return $(this).hasClass(formData['select-template']); |
| }).show(); |
| } else { |
| $step.find('.select-iso').show(); |
| } |
| var makeIsos = function(type, append) { |
| var $selects = makeSelects('templateid', args.data.templates[type], { |
| name: 'name', |
| desc: 'displaytext', |
| id: 'id' |
| }, { |
| 'wizard-field': 'template' |
| }); |
| var $templateHypervisor = $step.find('input[type=hidden][wizard-field=hypervisor]'); |
| |
| // Get hypervisor from template |
| if (type == 'featuredtemplates' || type == 'communitytemplates' || type == 'mytemplates' || type == 'sharedtemplates') { |
| $selects.each(function() { |
| var $select = $(this); |
| var template = $.grep(args.data.templates[type], function(tmpl, v) { |
| return tmpl.id == $select.find('input').val(); |
| })[0]; |
| |
| $select.change(function() { |
| $templateHypervisor |
| .attr('disabled', false) |
| .val(template.hypervisor); |
| }); |
| }); |
| } else { |
| $templateHypervisor.attr('disabled', 'disabled'); |
| } |
| |
| if (type == 'featuredisos' || type == 'communityisos' || type == 'myisos' || type == 'sharedisos') { |
| // Create hypervisor select |
| $selects.find('input').bind('click', function() { |
| var $select = $(this).closest('.select'); |
| |
| //$select.siblings().removeClass('selected').find('.hypervisor').remove(); //SelectISO has 3 tabs now. This line only remove hypervisor div in the same tab, not enough. The following 3 lines will remove hypervisor div in all of 3 tabs. |
| $("#instance-wizard-featured-isos .select-container div.selected").removeClass('selected').find('div.hypervisor').remove(); |
| $("#instance-wizard-community-isos .select-container div.selected").removeClass('selected').find('div.hypervisor').remove(); |
| $("#instance-wizard-my-isos .select-container div.selected").removeClass('selected').find('div.hypervisor').remove(); |
| $("#instance-wizard-shared-isos .select-container div.selected").removeClass('selected').find('div.hypervisor').remove(); |
| |
| $select.addClass('selected').append( |
| $('<div>').addClass('hypervisor') |
| .append($('<label>').html(_l('label.hypervisor') + ':')) |
| .append($('<select>').attr({ |
| name: 'hypervisorid' |
| })) |
| ); |
| |
| // Get hypervisor data |
| $(args.data.hypervisors).each(function() { |
| $select.find('select').append( |
| $('<option>').attr({ |
| value: this[args.hypervisor.idField], |
| 'wizard-field': 'hypervisor' |
| }) |
| .html(this[args.hypervisor.nameField]) |
| ); |
| }); |
| }); |
| } |
| |
| append($selects); |
| }; |
| |
| // Featured ISOs |
| $( |
| [ |
| // Templates |
| ['featuredtemplates', 'instance-wizard-featured-templates'], |
| ['communitytemplates', 'instance-wizard-community-templates'], |
| ['mytemplates', 'instance-wizard-my-templates'], |
| ['sharedtemplates', 'instance-wizard-shared-templates'], |
| |
| // ISOs |
| ['featuredisos', 'instance-wizard-featured-isos'], |
| ['communityisos', 'instance-wizard-community-isos'], |
| ['myisos', 'instance-wizard-my-isos'], |
| ['sharedisos', 'instance-wizard-shared-isos'] |
| //['isos', 'instance-wizard-all-isos'] |
| ] |
| ).each(function() { |
| var item = this; |
| var $selectContainer = $wizard.find('#' + item[1]).find('.select-container'); |
| |
| makeIsos(item[0], function($elem) { |
| $selectContainer.append($elem); |
| }); |
| }); |
| |
| var custom = args.customHidden({ |
| context: context, |
| data: args.data |
| }); |
| |
| $step.find('.custom-size-label').remove(); |
| |
| if (custom) { |
| $step.find('.section.custom-size').hide(); |
| $step.removeClass('custom-disk-size'); |
| } |
| |
| $step.find('input[type=radio]').bind('change', function() { |
| var $target = $(this); |
| var val = $target.val(); |
| var item = null; |
| if (item == null && args.data.templates.featuredtemplates != undefined) { |
| item = $.grep(args.data.templates.featuredtemplates, function(elem) { |
| return elem.id == val; |
| })[0]; |
| } |
| if (item == null && args.data.templates.communitytemplates != undefined) { |
| item = $.grep(args.data.templates.communitytemplates, function(elem) { |
| return elem.id == val; |
| })[0]; |
| } |
| if (item == null && args.data.templates.mytemplates!=undefined) { |
| item = $.grep(args.data.templates.mytemplates, function(elem) { |
| return elem.id == val; |
| })[0]; |
| } |
| if (item == null && args.data.templates.sharedtemplates!=undefined) { |
| item = $.grep(args.data.templates.sharedtemplates, function(elem) { |
| return elem.id == val; |
| })[0]; |
| } |
| |
| if (!item) return true; |
| |
| var hypervisor = item['hypervisor']; |
| if (hypervisor == 'KVM' || hypervisor == 'XenServer' || hypervisor == 'VMware') { |
| $step.find('.section.custom-size').show(); |
| $step.addClass('custom-disk-size'); |
| } else { |
| $step.find('.section.custom-size').hide(); |
| $step.removeClass('custom-disk-size'); |
| } |
| |
| return true; |
| }); |
| |
| originalValues(formData); |
| |
| } |
| } |
| }; |
| }, |
| |
| 'service-offering': function($step, formData) { |
| var originalValues = function(formData) { |
| if (formData.serviceofferingid) { |
| $step.find('input[type=radio]').filter(function() { |
| return $(this).val() == formData.serviceofferingid; |
| }).click(); |
| } else { |
| $step.find('input[type=radio]:first').click(); |
| } |
| }; |
| |
| return { |
| response: { |
| success: function(args) { |
| $step.find('.content .select-container').append( |
| makeSelects('serviceofferingid', args.data.serviceOfferings, { |
| name: 'name', |
| desc: 'displaytext', |
| id: 'id' |
| }, { |
| 'wizard-field': 'service-offering' |
| }) |
| ); |
| |
| $step.find('input[type=radio]').bind('change', function() { |
| var $target = $(this); |
| var val = $target.val(); |
| var item = $.grep(args.data.serviceOfferings, function(elem) { |
| return elem.id == val; |
| })[0]; |
| |
| if (!item) return true; |
| |
| var custom = item[args.customFlag]; |
| |
| if (custom) { |
| $step.addClass('custom-size'); |
| } else { |
| $step.removeClass('custom-size'); |
| } |
| |
| var customIops = item[args.customIopsFlag]; |
| |
| if (customIops && args.canShowCustomIops) { |
| $step.addClass('custom-iops'); |
| } else { |
| $step.removeClass('custom-iops'); |
| } |
| |
| return true; |
| }); |
| |
| originalValues(formData); |
| } |
| } |
| }; |
| }, |
| |
| 'data-disk-offering': function($step, formData) { |
| var originalValues = function(formData) { |
| var $targetInput = $step.find('input[type=radio]').filter(function() { |
| return $(this).val() == formData.diskofferingid; |
| }).click(); |
| |
| if (!$targetInput.size()) { |
| $step.find('input[type=radio]:visible').filter(':first').click(); |
| } |
| }; |
| |
| $step.find('.section.custom-size').hide(); |
| |
| return { |
| response: { |
| success: function(args) { |
| var multiDisk = args.multiDisk; |
| |
| $step.find('.multi-disk-select-container').remove(); |
| $step.removeClass('custom-disk-size'); |
| $step.find('.main-desc, p.no-datadisk').remove(); |
| |
| if (!multiDisk){ |
| if (args.required) { |
| $step.find('.section.no-thanks') |
| .hide(); |
| $step.addClass('required'); |
| } else { |
| $step.find('.section.no-thanks') |
| .show(); |
| $step.removeClass('required'); |
| } |
| } else { |
| $step.find('.section.no-thanks').hide(); |
| $step.addClass('required'); |
| } |
| |
| var $selectContainer = $step.find('.content .select-container:not(.multi-disk)'); |
| |
| if (multiDisk) { // Render as multiple groups for each disk |
| if (multiDisk[0].id == "none"){ |
| $step.find('.select-container').append( |
| $('<p>').addClass('no-datadisk').html(_l('message.no.datadisk')) |
| ); |
| return; |
| } |
| var $multiDiskSelect = $('<div>').addClass('multi-disk-select-container'); |
| |
| $(multiDisk).map(function(index, disk) { |
| var array_do = []; |
| $.each(args.data.diskOfferings, function( key, value ) { |
| if (value){ |
| if (value.disksize >= disk.size && value.name != "Custom"){ |
| array_do.push(value); |
| } |
| } |
| }) |
| var $group = $('<div>').addClass('disk-select-group'); |
| var $header = $('<div>').addClass('disk-select-header').append( |
| $('<div>').addClass('title').html(disk.label) |
| ).appendTo($group); |
| var $checkbox = $('<input>').addClass('multi-disk-select') |
| .attr({ |
| type: 'checkbox', |
| 'disk-id': disk.id |
| }) |
| .prependTo($header); |
| var $multiSelectContainer = $selectContainer.clone().append( |
| makeSelects('diskofferingid.' + disk.id, array_do, { |
| id: 'id', |
| name: 'name', |
| desc: 'displaytext' |
| }, { |
| 'wizard-field': 'disk-offering' |
| }) |
| ).appendTo($group).addClass('multi-disk'); |
| |
| $group.appendTo($multiDiskSelect); |
| $group.data('json-obj', disk); |
| |
| // Show-hide disk group selects |
| $checkbox.click(function() { |
| $group.toggleClass('selected'); |
| $group.find('.select:first input[type=radio]').click(); |
| |
| if (!$multiDiskSelect.find('input[type=checkbox]:checked').size()) { |
| $step.find('.no-thanks input[type=radio]').click(); |
| } else { |
| $step.find('.no-thanks input[type=radio]').attr('checked', false); |
| } |
| }); |
| |
| // Add custom disk size box |
| $step.find('.section.custom-size').clone().hide().appendTo($group); |
| }); |
| |
| $multiDiskSelect.insertAfter($selectContainer); |
| $selectContainer.hide(); |
| |
| // Fix issue with containers always showing after reload |
| $multiDiskSelect.find('.select-container').attr('style', null); |
| } else { |
| $selectContainer.show(); |
| $step.find('.content .select-container').append( |
| makeSelects('diskofferingid', args.data.diskOfferings, { |
| id: 'id', |
| name: 'name', |
| desc: 'displaytext' |
| }, { |
| 'wizard-field': 'disk-offering' |
| }) |
| ); |
| } |
| |
| $step.find('input[type=radio]').bind('change', function() { |
| var $target = $(this); |
| var val = $target.val(); |
| var item = $.grep(args.data.diskOfferings, function(elem) { |
| return elem.id == val; |
| })[0]; |
| var isMultiDisk = $step.find('.multi-disk-select').size(); |
| |
| // Uncheck any multi-select groups |
| if ($target.closest('.no-thanks').size() && isMultiDisk) { |
| $step.find('.disk-select-group input[type=checkbox]:checked').click(); |
| $(this).attr('checked', true); |
| |
| return true; |
| } |
| |
| if (!item) { |
| if (isMultiDisk) { |
| $(this).closest('.disk-select-group .section.custom-size').hide(); |
| $(this).closest('.disk-select-group').removeClass('custom-size'); |
| } else { |
| // handle removal of custom size controls |
| $step.find('.section.custom-size').hide(); |
| $step.removeClass('custom-disk-size'); |
| |
| // handle removal of custom IOPS controls |
| $step.removeClass('custom-iops-do'); |
| } |
| |
| return true; |
| } |
| |
| var custom = item[args.customFlag]; |
| |
| if (!isMultiDisk) $step.find('.custom-size-label').remove(); |
| |
| if (custom && !isMultiDisk) { |
| $target.parent().find('.name') |
| .append( |
| $('<span>').addClass('custom-size-label') |
| .append(': ') |
| .append( |
| $('<span>').addClass('custom-disk-size').html( |
| $step.find('.custom-size input[name=size]').val() |
| ) |
| ) |
| .append(' GB') |
| ); |
| $target.parent().find('.select-desc .desc') |
| .append( |
| $('<span>').addClass('custom-size-label') |
| .append(', ') |
| .append( |
| $('<span>').addClass('custom-disk-size').html( |
| $step.find('.custom-size input[name=size]').val() |
| ) |
| ) |
| .append(' GB') |
| ); |
| $step.find('.section.custom-size').show(); |
| $step.addClass('custom-disk-size'); |
| $target.closest('.select-container').scrollTop( |
| $target.position().top |
| ); |
| } else if (custom && isMultiDisk) { |
| $(this).closest('.disk-select-group').addClass('custom-size'); |
| } else { |
| if (isMultiDisk) { |
| $(this).closest('.disk-select-group').removeClass('custom-size'); |
| } else { |
| $step.find('.section.custom-size').hide(); |
| $step.removeClass('custom-disk-size'); |
| } |
| } |
| |
| var customIops = item[args.customIopsDoFlag]; |
| |
| if (customIops) { |
| $step.addClass('custom-iops-do'); |
| } else { |
| $step.removeClass('custom-iops-do'); |
| } |
| |
| return true; |
| }); |
| |
| originalValues(formData); |
| } |
| } |
| }; |
| }, |
| |
| 'affinity': function($step, formData) { |
| return { |
| response: { |
| success: function(args) { |
| // Cleanup |
| $step.find('.main-desc, p.no-affinity-groups').remove(); |
| |
| if (args.data.affinityGroups && args.data.affinityGroups.length) { |
| $step.prepend( |
| $('<div>').addClass('main-desc').append( |
| $('<p>').html(_l('message.select.affinity.groups')) |
| ) |
| ); |
| $step.find('.select-container').append( |
| makeSelects( |
| 'affinity-groups', |
| args.data.affinityGroups, { |
| name: 'name', |
| desc: 'description', |
| id: 'id' |
| }, { |
| type: 'checkbox', |
| 'wizard-field': 'affinity-groups' |
| }, |
| args.data.selectedObj, |
| args.data.selectedObjNonEditable |
| ) |
| ); |
| } else { |
| $step.find('.select-container').append( |
| $('<p>').addClass('no-affinity-groups').html(_l('message.no.affinity.groups')) |
| ); |
| } |
| } |
| } |
| }; |
| }, |
| |
| 'sshkeyPairs': function($step, formData) { |
| var originalValues = function(formData) { |
| if (formData.sshkeypair) { |
| $step.find('input[type=radio]').filter(function() { |
| return $(this).val() == formData.sshkeypair; |
| }).click(); |
| } else { |
| $step.find('input[type=radio]:first').click(); |
| } |
| }; |
| return { |
| response: { |
| success: function(args) { |
| $step.find('.main-desc, p.no-sshkey-pairs').remove(); |
| |
| if (args.data.sshkeyPairs && args.data.sshkeyPairs.length) { |
| $step.prepend( |
| $('<div>').addClass('main-desc').append( |
| $('<p>').html(_l('message.please.select.ssh.key.pair.use.with.this.vm')) |
| ) |
| ); |
| $step.find('.section.no-thanks').show(); |
| $step.find('.select-container').append( |
| makeSelects( |
| 'sshkeypair', |
| args.data.sshkeyPairs, { |
| name: 'name', |
| id: 'name' |
| }, { |
| 'wizard-field': 'sshkey-pairs' |
| } |
| ) |
| ); |
| originalValues(formData); // if we can select only one. |
| } else { |
| $step.find('.section.no-thanks').hide(); |
| $step.find('.select-container').append( |
| $('<p>').addClass('no-sshkey-pairs').html(_l('You do not have any ssh key pairs. Please continue to the next step.')) |
| ); |
| } |
| } |
| } |
| }; |
| }, |
| |
| 'network': function($step, formData) { |
| var showAddNetwork = true; |
| |
| var checkShowAddNetwork = function($newNetwork) { |
| if (!showAddNetwork) { |
| $newNetwork.hide(); |
| } else { |
| $newNetwork.show(); |
| } |
| }; |
| |
| var originalValues = function(formData) { |
| // Default networks |
| $step.find('input[type=radio]').filter(function() { |
| return $(this).val() == formData['defaultNetwork']; |
| }).click(); |
| |
| // Optional networks |
| var selectedOptionalNetworks = []; |
| |
| if ($.isArray(formData['shared-networks']) != -1) { |
| $(formData['shared-networks']).each(function() { |
| selectedOptionalNetworks.push(this); |
| }); |
| } else { |
| selectedOptionalNetworks.push(formData['shared-networks']); |
| } |
| |
| var $checkboxes = $step.find('input[name=shared-networks]'); |
| $(selectedOptionalNetworks).each(function() { |
| var networkID = this; |
| $checkboxes.filter(function() { |
| return $(this).val() == networkID; |
| }).attr('checked', 'checked'); |
| }); |
| }; |
| |
| var $newNetwork = $step.find('.new-network'); |
| var $newNetworkCheckbox = $newNetwork.find('input[type=checkbox]'); |
| |
| // Setup new network field |
| $newNetworkCheckbox.unbind('click'); |
| $newNetworkCheckbox.click(function() { |
| $newNetwork.toggleClass('unselected'); |
| |
| // Select another default if hiding field |
| if ($newNetwork.hasClass('unselected')) { |
| $step.find('input[type=radio]:visible:first').click(); |
| } else { |
| $newNetwork.find('input[type=radio]').click(); |
| } |
| }); |
| |
| setTimeout(function() { |
| var $checkbox = $step.find('.new-network input[type=checkbox]'); |
| var $newNetwork = $checkbox.closest('.new-network'); |
| |
| if ($step.find('.select.my-networks .select-container .select:visible').size()) { |
| $checkbox.attr('checked', false); |
| $newNetwork.addClass('unselected'); |
| } else { |
| $newNetwork.find('input[name=defaultNetwork]').filter('[value=new-network]').click(); |
| } |
| |
| $checkbox.change(); |
| }); |
| |
| // Show relevant conditional sub-step if present |
| $step.find('.wizard-step-conditional').hide(); |
| |
| if ($.isFunction(args.showAddNetwork)) { |
| showAddNetwork = args.showAddNetwork({ |
| data: formData, |
| context: context |
| }); |
| } |
| |
| // Filter network list by VPC ID |
| var filterNetworkList = function(vpcID) { |
| var $selects = $step.find('.my-networks .select-container .select'); |
| var $visibleSelects = $($.grep($selects, function(select) { |
| var $select = $(select); |
| |
| return args.vpcFilter($select.data('json-obj'), vpcID); |
| })); |
| var $addNetworkForm = $step.find('.select.new-network'); |
| var $addNewNetworkCheck = $addNetworkForm.find('input[name=new-network]'); |
| |
| // VPC networks cannot be created via instance wizard |
| if (vpcID != -1) { |
| $step.find('.my-networks .select-container').addClass('single-select'); |
| $addNetworkForm.hide(); |
| |
| if ($addNewNetworkCheck.is(':checked')) { |
| $addNewNetworkCheck.click(); |
| $addNewNetworkCheck.attr('checked', false); |
| } |
| } else { |
| $step.find('.my-networks .select-container').removeClass('single-select'); |
| $addNetworkForm.show(); |
| checkShowAddNetwork($addNetworkForm); |
| } |
| |
| $selects.find('input[type=checkbox]').attr('checked', false); |
| $selects.hide(); |
| $visibleSelects.show(); |
| |
| // Select first visible item by default |
| $visibleSelects.filter(':first') |
| .find('input[type=radio]') |
| .click(); |
| |
| cloudStack.evenOdd($visibleSelects, 'div.select', { |
| even: function($elem) { |
| $elem.removeClass('odd'); |
| $elem.addClass('even'); |
| }, |
| odd: function($elem) { |
| $elem.removeClass('even'); |
| $elem.addClass('odd'); |
| } |
| }); |
| }; |
| |
| var $vpcSelect = $step.find('select[name=vpc-filter]'); |
| |
| $vpcSelect.unbind('change'); |
| $vpcSelect.change(function() { |
| filterNetworkList($vpcSelect.val()); |
| }); |
| |
| return { |
| response: { |
| success: function(args) { |
| var vpcs = args.data.vpcs; |
| var addClass = args.addClass; |
| var removeClass = args.removeClass; |
| |
| // Populate VPC drop-down |
| $vpcSelect.html(''); |
| $(vpcs).map(function(index, vpc) { |
| var $option = $('<option>'); |
| var id = vpc.id; |
| var description = vpc.name; |
| |
| $option.attr('value', id); |
| $option.html(description); |
| $option.appendTo($vpcSelect); |
| }); |
| |
| // 'No VPC' option |
| $('<option>').attr('value', '-1').html(_l('ui.listView.filters.all')).prependTo($vpcSelect); |
| |
| $vpcSelect.val(-1); |
| |
| // Populate network offering drop-down |
| $(args.data.networkOfferings).each(function() { |
| $('<option>') |
| .val(this.id) |
| .html(this.name) |
| .appendTo($newNetwork.find('select')); |
| }); |
| |
| if (args.type) { |
| $step.find('.wizard-step-conditional').filter(function() { |
| return $(this).hasClass(args.type); |
| }).show(); |
| } else { |
| $step.find('.select-network').show(); |
| } |
| |
| // My networks |
| $step.find('.my-networks .select-container').append( |
| makeSelects('my-networks', args.data.networkObjs, { |
| name: 'name', |
| desc: 'type', |
| id: 'id' |
| }, { |
| type: 'checkbox', |
| 'wizard-field': 'my-networks', |
| secondary: { |
| desc: 'Default', |
| name: 'defaultNetwork', |
| type: 'radio', |
| 'wizard-field': 'default-network' |
| } |
| }) |
| ); |
| |
| // Add IP/advanced option fields |
| $step.find('.my-networks .select-container .select, .select.new-network .select').each(function () { |
| var $select = $(this); |
| var $advancedLink = $('<div>').addClass('advanced-options hide-if-unselected'); |
| var $specifyIpField = $('<div>').addClass('specify-ip hide-if-unselected').append( |
| $('<label>').html(_l('label.ip.address')), |
| $('<input>').attr({ type: 'text' }) |
| ); |
| |
| // Cleanup |
| if ($select.closest('.new-network').size()) { |
| $select.find('.advanced-options, .specify-ip').remove(); |
| } |
| |
| $select.append($advancedLink, $specifyIpField); |
| $advancedLink.click(function() { |
| $select.toggleClass('advanced'); |
| }); |
| }); |
| |
| // Show non-VPC networks by default |
| filterNetworkList(-1); |
| |
| // Security groups (alt. page) |
| var $sgSelects = makeSelects('security-groups', args.data.securityGroups, { |
| name: 'name', |
| desc: 'description', |
| id: 'id' |
| }, { |
| type: 'checkbox', |
| 'wizard-field': 'security-groups' |
| }); |
| $step.find('.security-groups .select-container').append($sgSelects); |
| |
| //If there is only one security group and the only one is 'default', make it selected by default |
| if ($sgSelects.length == 1) { |
| var $firstCheckbox = $sgSelects.eq(0); |
| if ($firstCheckbox.find('div .name').text() == 'default') { |
| $firstCheckbox.find('input:checkbox').click(); |
| } |
| } |
| |
| originalValues(formData); |
| checkShowAddNetwork($newNetwork); |
| } |
| } |
| }; |
| }, |
| |
| 'review': function($step, formData) { |
| $step.find('[wizard-field]').each(function() { |
| var field = $(this).attr('wizard-field'); |
| var fieldName; |
| var $input = $wizard.find('[wizard-field=' + field + ']').filter(function() { |
| return ($(this).is(':selected') || |
| $(this).is(':checked') || |
| $(this).attr('type') == 'hidden') && |
| $(this).is(':not(:disabled)'); |
| }); |
| |
| if ($input.is('option')) { |
| fieldName = $input.html(); |
| } else if ($input.is('input[type=radio]')) { |
| // Choosen New network as default |
| if ($input.parents('div.new-network').size()) { |
| fieldName = $input.closest('div.new-network').find('input[name="new-network-name"]').val(); |
| // Choosen Network from existed |
| } else if ($input.parents('div.my-networks').size()) { |
| fieldName = $input.closest('div.select').find('.select-desc .name').html(); |
| } else { |
| fieldName = $input.parent().find('.select-desc .name').html(); |
| } |
| } else if ($input.eq(0).is('input[type=checkbox]')) { |
| fieldName = ''; |
| $input.each(function(index) { |
| if (index != 0) fieldName += '<br />'; |
| fieldName += $(this).next('div.select-desc').find('.name').html(); |
| }); |
| } else if ($input.is('input[type=hidden]')) { |
| fieldName = $input.val(); |
| } |
| |
| if (fieldName) { |
| $(this).html(fieldName); |
| } else { |
| $(this).html('(' + _l('label.none') + ')'); |
| } |
| |
| var conditionalFieldFrom = $(this).attr('conditional-field'); |
| if (conditionalFieldFrom) { |
| if ($wizard.find('.' + conditionalFieldFrom).css('display') == 'block') { |
| $(this).closest('div.select').show(); |
| } else { |
| $(this).closest('div.select').hide(); |
| } |
| } |
| }); |
| } |
| }; |
| |
| $wizard.bind('cloudStack.instanceWizard.showStep', function(e, args) { |
| showStep(args.index, { refresh: true }); |
| }); |
| |
| // Go to specified step in wizard, |
| // updating nav items and diagram |
| var showStep = function(index, options) { |
| if (!options) options = {}; |
| var targetIndex = index - 1; |
| |
| if (index <= 1) targetIndex = 0; |
| if (targetIndex == $steps.size()) { |
| completeAction(); |
| return; |
| } |
| |
| var $targetStep = $($steps.hide()[targetIndex]).show(); |
| var stepID = $targetStep.attr('wizard-step-id'); |
| var formData = cloudStack.serializeForm($form); |
| |
| if (options.refresh) { |
| $targetStep.removeClass('loaded'); |
| } |
| |
| if (!$targetStep.hasClass('loaded')) { |
| // Remove previous content |
| if (!$targetStep.hasClass('review')) { // Review row content is not autogenerated |
| $targetStep.find('.select-container:not(.fixed) div, option:not(:disabled)').remove(); |
| } |
| |
| dataProvider( |
| index, |
| dataGenerators[stepID]( |
| $targetStep, |
| formData |
| ) |
| ); |
| |
| if (!$targetStep.hasClass('repeat') && !$targetStep.hasClass('always-load')) $targetStep.addClass('loaded'); |
| } |
| |
| // Show launch vm button if last step |
| var $nextButton = $wizard.find('.button.next'); |
| $nextButton.find('span').html(_l('label.next')); |
| $nextButton.removeClass('final'); |
| if ($targetStep.hasClass('review')) { |
| $nextButton.find('span').html(_l('label.launch.vm')); |
| $nextButton.addClass('final'); |
| } |
| |
| // Hide previous button on first step |
| var $previousButton = $wizard.find('.button.previous'); |
| if (index == 1) $previousButton.hide(); |
| else $previousButton.show(); |
| |
| // Update progress bar |
| var $targetProgress = $progress.removeClass('active').filter(function() { |
| return $(this).index() <= targetIndex; |
| }).toggleClass('active'); |
| |
| // Update diagram; show/hide as necessary |
| $diagramParts.filter(function() { |
| return $(this).index() <= targetIndex; |
| }).fadeIn('slow'); |
| $diagramParts.filter(function() { |
| return $(this).index() > targetIndex; |
| }).fadeOut('slow'); |
| |
| setTimeout(function() { |
| if (!$targetStep.find('input[type=radio]:checked').size()) { |
| $targetStep.find('input[type=radio]:first').click(); |
| } |
| }, 50); |
| }; |
| |
| // Events |
| $wizard.click(function(event) { |
| var $target = $(event.target); |
| var $activeStep = $form.find('.step:visible'); |
| |
| // Next button |
| if ($target.closest('div.button.next').size()) { |
| //step 2 - select template/ISO |
| if($activeStep.hasClass('select-iso')) { |
| if ($activeStep.find('.content:visible input:checked').size() == 0) { |
| cloudStack.dialog.notice({ |
| message: 'message.step.1.continue' |
| }); |
| return false; |
| } |
| $(window).trigger("cloudStack.module.instanceWizard.clickNextButton", { |
| $form: $form, |
| currentStep: 2 |
| }); |
| } |
| |
| //step 6 - select network |
| if ($activeStep.find('.wizard-step-conditional.select-network:visible').size() > 0) { |
| var data = $activeStep.data('my-networks'); |
| |
| if (!data) { |
| $activeStep.closest('form').data('my-networks', cloudStack.serializeForm( |
| $activeStep.closest('form') |
| )['my-networks']); |
| } |
| |
| if ($activeStep.find('input[type=checkbox]:checked').size() == 0) { //if no checkbox is checked |
| cloudStack.dialog.notice({ |
| message: 'message.step.4.continue' |
| }); |
| return false; |
| } |
| |
| if ($activeStep.hasClass('next-use-security-groups')) { |
| var advSGFilter = args.advSGFilter({ |
| data: cloudStack.serializeForm($form) |
| }); |
| |
| if (advSGFilter == 0) { //when total number of selected sg networks is 0, then 'Select Security Group' is skipped, go to step 6 directly |
| showStep(6); |
| } else { //when total number of selected sg networks > 0 |
| if ($activeStep.find('input[type=checkbox]:checked').size() > 1) { //when total number of selected networks > 1 |
| cloudStack.dialog.notice({ |
| message: "Can't create a vm with multiple networks one of which is Security Group enabled" |
| }); |
| return false; |
| } |
| } |
| } |
| } |
| |
| //step 6 - review (spcifiy displyname, group as well) |
| if ($activeStep.hasClass('review')) { |
| if ($activeStep.find('input[name=displayname]').size() > 0 && $activeStep.find('input[name=displayname]').val().length > 0) { |
| //validate |
| var b = cloudStack.validate.vmHostName($activeStep.find('input[name=displayname]').val()); |
| if (b == false) |
| return false; |
| } |
| } |
| |
| if (!$form.valid()) { |
| if ($form.find('input.error:visible, select.error:visible').size()) { |
| return false; |
| } |
| } |
| |
| if ($activeStep.hasClass('repeat')) { |
| showStep($steps.filter(':visible').index() + 1); |
| } else { |
| showStep($steps.filter(':visible').index() + 2); |
| } |
| |
| return false; |
| } |
| |
| // Previous button |
| if ($target.closest('div.button.previous').size()) { |
| var $step = $steps.filter(':visible'); |
| var $networkStep = $steps.filter('.network'); |
| var index = $step.index(); |
| |
| $networkStep.removeClass('next-use-security-groups'); |
| |
| if (index) { |
| if (index == $steps.size() - 1 && $networkStep.hasClass('next-use-security-groups')) { |
| showStep(5); |
| } else if ($activeStep.find('.select-security-group:visible').size() && |
| $activeStep.find('.select-network.no-add-network').size()) { |
| showStep(5); |
| } else { |
| showStep(index); |
| } |
| } |
| |
| return false; |
| } |
| |
| // Close button |
| if ($target.closest('div.button.cancel').size()) { |
| close(); |
| |
| return false; |
| } |
| |
| // Edit link |
| if ($target.closest('div.edit').size()) { |
| var $edit = $target.closest('div.edit'); |
| |
| showStep($edit.find('a').attr('href')); |
| |
| return false; |
| } |
| |
| return true; |
| }); |
| |
| showStep(1); |
| |
| $wizard.bind('change', function(event) { |
| var $target = $(event.target); |
| var $step = $target.closest('.step'); |
| var $futureSteps = $step.siblings().filter(function() { |
| return $(this).index() > $step.index(); |
| }); |
| |
| // Reset all fields in futher steps |
| $futureSteps.removeClass('loaded'); |
| }); |
| |
| var minCustomDiskSize = args.minDiskOfferingSize ? |
| args.minDiskOfferingSize() : 1; |
| |
| var maxCustomDiskSize = args.maxDiskOfferingSize ? |
| args.maxDiskOfferingSize() : 100; |
| |
| // Setup tabs and slider |
| $wizard.find('.section.custom-size.custom-disk-size .size.min span').html(minCustomDiskSize); |
| $wizard.find('.section.custom-size.custom-disk-size input[type=text]').val(minCustomDiskSize); |
| $wizard.find('.section.custom-size.custom-disk-size .size.max span').html(maxCustomDiskSize); |
| $wizard.find('.tab-view').tabs(); |
| $wizard.find('.slider').each(function() { |
| var $slider = $(this); |
| |
| $slider.slider({ |
| min: minCustomDiskSize, |
| max: maxCustomDiskSize, |
| start: function(event) { |
| $slider.closest('.section.custom-size').find('input[type=radio]').click(); |
| }, |
| slide: function(event, ui) { |
| $slider.closest('.section.custom-size').find('input[type=text]').val( |
| ui.value |
| ); |
| $slider.closest('.step').find('span.custom-disk-size').html( |
| ui.value |
| ); |
| } |
| }); |
| }); |
| |
| $wizard.find('div.data-disk-offering div.custom-size input[type=text]').bind('change', function() { |
| var old = $wizard.find('div.data-disk-offering div.custom-size input[type=text]').val(); |
| $wizard.find('div.data-disk-offering span.custom-disk-size').html(_s(old)); |
| }); |
| |
| |
| return $wizard.dialog({ |
| title: _l('label.vm.add'), |
| width: 896, |
| height: 570, |
| closeOnEscape: false, |
| zIndex: 5000 |
| }) |
| .closest('.ui-dialog').overlay(); |
| }; |
| |
| instanceWizard(args); |
| }; |
| }; |
| })(jQuery, cloudStack); |