// 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) { var installWizard = function(args) { var context = args.context; var $installWizard = $('
').addClass('install-wizard'); var $container = args.$container; var state = {}; // Hold wizard form state var launchStart; // Holds last launch callback, in case of error var $launchState; /** * Successful installation action */ var complete = function() { $installWizard.remove(); $('html body').removeClass('install-wizard'); args.complete(); }; /** * Retrive copy text and append to element -- async * @param id * @param $elem */ var getCopy = function(id, $elem) { cloudStack.installWizard.copy[id]({ response: { success: function(args) { $elem.append(_l(args.text)); } } }); return $elem; }; /** * Go to specified step in flow -- for use in individual steps * @param stateStepID ID to group state elements in (i.e., zone, pod, cluster, ...) * @param $elem (optional) Element containing
, to serialize for state */ var goTo = cloudStack._goto = function(stepID, stateID, $elem, options) { if (!options) options = {}; var $body = $installWizard.find('.body'); if (stateID && $elem) { state[stateID] = cloudStack.serializeForm($elem.is('form') ? $elem : $elem.find('form')); } $body.children().fadeOut('fast', function() { var $nextStep = steps[stepID]({ nextStep: options.nextStep }); $body.children().detach(); $nextStep.appendTo($body).hide(); $nextStep.addClass('step').fadeIn(); }); }; /** * Generic page elements */ var elems = { /** * A standard intro text wizard step template */ stepIntro: function(args) { var title = args.title; var subtitle = args.subtitle; var copyID = args.copyID; var prevStepID = args.prevStepID; var nextStepID = args.nextStepID; var diagram = args.diagram; var $intro = $('
').addClass('intro'); var $title = $('
').addClass('title') .html(title); var $subtitle = $('
').addClass('subtitle') .html(subtitle); var $copy = getCopy(copyID, $('

')); var $prev = elems.prevButton(_l('label.back')); var $continue = elems.nextButton('OK'); $continue.click(function() { goTo(nextStepID); return false; }); $prev.click(function() { goTo(prevStepID); }); return function(args) { showDiagram(diagram); return $intro.append( $title, $subtitle, $copy, $prev, $continue ); }; }, /** * A standard form-based wizard step template * -- relies on createForm for form generation */ step: function(args) { var title = args.title; var formData = args.form; var diagram = args.diagram; var id = args.id; var stateID = args.stateID; var tooltipID = args.tooltipID; var prevStepID = args.prevStepID; var nextStepID = args.nextStepID; var form; var $container = $('
').addClass(id); var $form = $('
').addClass('setup-form'); var $save = elems.nextButton(_l('label.continue'), { type: 'submit' }); var $prev = elems.prevButton(_l('label.back')); var $title = $('
').addClass('title').html(_l(title)); // Generate form form = cloudStack.dialog.createForm({ noDialog: true, form: { title: title, desc: '', fields: formData }, after: function(args) { goTo(nextStepID, stateID, $form); } }); $form.append(form.$formContainer); $form.find('.form-item').addClass('field'); $prev.appendTo($form.find('form')); $save.appendTo($form.find('form')); // Submit handler $form.find('form').submit(function() { form.completeAction($form); return false; }); // Go back handler $prev.click(function(event) { goTo(prevStepID); }); // Cleanup $form.find('.message').remove(); $form.find('label.error').hide(); $container.append($form.prepend($title)); showTooltip($form, tooltipID); return function(args) { var overrideGotoEvent = function(event) { goTo(args.nextStep, stateID, $form); return false; }; if (args && args.nextStep) { $save.unbind('click'); $save.click(overrideGotoEvent); } // Setup diagram, tooltips showDiagram(diagram); setTimeout(function() { $form.find('input[type=text]:first').focus(); }, 600); return $container; }; }, /** * A form item tooltip */ tooltip: function(title, content) { return $('
').addClass('tooltip-info').append( $('
').addClass('arrow'), $('
').addClass('title').html(_l(title)), $('
').addClass('content').append($('

').html(_l(content))) ); }, /** * The main header */ header: function() { return $('

').addClass('header') .append( $.merge( $('

').html(_l('label.installWizard.title')), $('

').html(_l('label.installWizard.subtitle')) ) ); }, /** * The wizard body (contains form) */ body: function() { return $('
').addClass('body'); }, /** * A standard next button */ nextButton: function(label, options) { var $button = options && !options.type ? $('
').addClass('button goTo').html(label) : $('').attr({ type: 'submit' }).addClass('button goTo').val(label); return $button; }, /** * A standard previous/go back button */ prevButton: function(label) { return $('
').addClass('button go-back').html(label); }, diagramParts: function() { return $('
').addClass('diagram').append( $('
').addClass('part zone'), $('
').addClass('part pod'), $('
').addClass('part cluster'), $('
').addClass('part host'), $('
').addClass('part primaryStorage'), $('
').addClass('part secondaryStorage'), $('
').addClass('part loading').append($('
').addClass('icon')) ); } }; var $diagramParts = elems.diagramParts(); var showDiagram = function(parts) { $diagramParts.children().hide(); $diagramParts.find(parts).show(); }; /** * Show tooltip for focused form elements */ var showTooltip = function($formContainer, sectionID) { var $tooltip = elems.tooltip(_l('label.hints'), ''); $formContainer.find('input[type=text]').focus(function() { var $input = $(this); $tooltip.find('p').html(''); $tooltip.appendTo($formContainer); $tooltip.css({ top: $(this).position().top - 20 }); var content = getCopy( 'tooltip.' + sectionID + '.' + $input.attr('name'), $tooltip.find('p') ); }); $formContainer.find('input').blur(function() { $tooltip.remove(); }); }; /** * Layout/behavior for each step in wizard */ var steps = { intro: function(args) { var $intro = $('
').addClass('intro what-is-cloudstack'); var $title = $('
').addClass('title').html(_l('label.what.is.cloudstack')); var $subtitle = $('
').addClass('subtitle').html(_l('label.introduction.to.cloudstack')); var $copy = getCopy('whatIsCloudStack', $('

')); var $continue = elems.nextButton(_l('label.continue.basic.install')); var $advanced = elems.nextButton(_l('label.skip.guide')).addClass('advanced-installation'); $continue.click(function() { goTo('changeUser'); return false; }); $advanced.click(function() { complete(); return false; }); return $intro.append($title, $subtitle, $copy, $advanced, $continue); }, changeUser: function(args) { var $changeUser = $('
').addClass('step change-user'); var $form = $('').appendTo($changeUser); // Fields var $password = $('').addClass('required').attr({ id: 'password', type: 'password', name: 'password' }); var $passwordConfirm = $('').addClass('required').attr({ id: 'password-confirm', type: 'password', name: 'password-confirm' }); var $save = elems.nextButton(_l('label.save.and.continue'), { type: 'submit' }); $form.append( $('
').addClass('title').html(_l('message.change.password')), $('
').addClass('field').append( $(''), $password ), $('
').addClass('field').append( $(''), $passwordConfirm ), $save ); $form.validate({ rules: { 'password-confirm': { equalTo: '#password' } }, messages: { 'password-confirm': { equalTo: _l('error.password.not.match') } } }); // Save event $form.submit(function() { if (!$form.valid()) return false; var $loading = $('
').addClass('loading-overlay').prependTo($form); cloudStack.installWizard.changeUser({ data: cloudStack.serializeForm($form), response: { success: function(args) { goTo('addZoneIntro', 'newUser', $form); } } }); return false; }); showDiagram(''); return $changeUser; }, /** * Add zone intro text * @param args */ addZoneIntro: elems.stepIntro({ title: _l('label.installWizard.addZoneIntro.title'), subtitle: _l('label.installWizard.addZoneIntro.subtitle'), copyID: 'whatIsAZone', prevStepID: 'changeUser', nextStepID: 'addZone', diagram: '.part.zone' }), /** * Add zone form */ addZone: elems.step({ title: _l('label.installWizard.addZone.title'), id: 'add-zone', stateID: 'zone', tooltipID: 'addZone', diagram: '.part.zone', prevStepID: 'addZoneIntro', nextStepID: 'addPodIntro', form: { name: { label: 'label.name', validation: { required: true } }, ip4dns1: { label: 'label.dns.1', validation: { required: true } }, ip4dns2: { label: 'label.dns.2' }, internaldns1: { label: 'label.internal.dns.1', validation: { required: true } }, internaldns2: { label: 'label.internal.dns.2' } } }), /** * Add pod intro text * @param args */ addPodIntro: elems.stepIntro({ title: _l('label.installWizard.addPodIntro.title'), subtitle: _l('label.installWizard.addPodIntro.subtitle'), copyID: 'whatIsAPod', prevStepID: 'addZone', nextStepID: 'addPod', diagram: '.part.zone, .part.pod' }), /** * Add pod form * @param args */ addPod: elems.step({ title: _l('label.add.pod'), id: 'add-pod', stateID: 'pod', tooltipID: 'addPod', diagram: '.part.zone, .part.pod', prevStepID: 'addPodIntro', nextStepID: 'configureGuestTraffic', form: { name: { label: 'label.name', validation: { required: true }}, reservedSystemGateway: { label: 'label.gateway', validation: { required: true }}, reservedSystemNetmask: { label: 'label.netmask', validation: { required: true }}, ipRange: { label: 'label.ip.range', range: ['reservedSystemStartIp', 'reservedSystemEndIp'], validation: { required: true } } } }), /** * Add guest network form */ configureGuestTraffic: elems.step({ title: _l('label.add.guest.network'), id: 'add-guest-network', stateID: 'guestTraffic', tooltipID: 'configureGuestTraffic', diagram: '.part.zone, .part.pod', prevStepID: 'addPod', nextStepID: 'addClusterIntro', form: { guestGateway: { label: 'label.gateway', validation: { required: true } }, guestNetmask: { label: 'label.netmask', validation: { required: true } }, guestIPRange: { label: 'label.ip.range', range: ['guestStartIp', 'guestEndIp'], validation: { required: true } } } }), /** * Add cluster intro text * @param args */ addClusterIntro: elems.stepIntro({ title: _l('label.installWizard.addClusterIntro.title'), subtitle: _l('label.installWizard.addClusterIntro.subtitle'), copyID: 'whatIsACluster', prevStepID: 'configureGuestTraffic', nextStepID: 'addCluster', diagram: '.part.zone, .part.cluster' }), /** * Add cluster form * @param args */ addCluster: elems.step({ title: _l('label.add.cluster'), id: 'add-cluster', stateID: 'cluster', tooltipID: 'addCluster', prevStepID: 'addClusterIntro', nextStepID: 'addHostIntro', diagram: '.part.zone, .part.cluster', form: { hypervisor: { label: 'label.hypervisor', select: function(args) { args.response.success({ data: [ { id: 'XenServer', description: 'XenServer' }, { id: 'KVM', description: 'KVM'} ]}); } }, name: { label: 'label.name', validation: { required: true }} } }), /** * Add host intro text * @param args */ addHostIntro: elems.stepIntro({ title: _l('label.installWizard.addHostIntro.title'), subtitle: _l('label.installWizard.addHostIntro.subtitle'), copyID: 'whatIsAHost', prevStepID: 'addCluster', nextStepID: 'addHost', diagram: '.part.zone, .part.host' }), /** * Add host form * @param args */ addHost: elems.step({ title: _l('label.add.host'), id: 'add-host', stateID: 'host', tooltipID: 'addHost', prevStepID: 'addHostIntro', nextStepID: 'addPrimaryStorageIntro', diagram: '.part.zone, .part.host', form: { hostname: { label: 'label.host.name', validation: { required: true } }, username: { label: 'label.username', validation: { required: true } }, password: { label: 'label.password', validation: { required: true }, isPassword: true } } }), /** * Add primary storage intro text * @param args */ addPrimaryStorageIntro: elems.stepIntro({ title: _l('label.installWizard.addPrimaryStorageIntro.title'), subtitle: _l('label.installWizard.addPrimaryStorageIntro.subtitle'), copyID: 'whatIsPrimaryStorage', prevStepID: 'addHost', nextStepID: 'addPrimaryStorage', diagram: '.part.zone, .part.primaryStorage' }), /** * Add primary storage * @param args */ addPrimaryStorage: elems.step({ title: _l('label.add.primary.storage'), id: 'add-primary-storage', stateID: 'primaryStorage', tooltipID: 'addPrimaryStorage', prevStepID: 'addPrimaryStorageIntro', nextStepID: 'addSecondaryStorageIntro', diagram: '.part.zone, .part.primaryStorage', form: { name: { label: 'label.name', validation: { required: true } }, protocol: { label: 'label.protocol', select: function(args) { args.response.success({ data: { id: 'nfs', description: 'NFS' } }); } }, server: { label: 'label.server', validation: { required: true } }, path: { label: 'label.path', validation: { required: true } } } }), /** * Add secondary storage intro text * @param args */ addSecondaryStorageIntro: elems.stepIntro({ title: _l('label.installWizard.addSecondaryStorageIntro.title'), subtitle: _l('label.installWizard.addSecondaryStorageIntro.subtitle'), copyID: 'whatIsSecondaryStorage', prevStepID: 'addPrimaryStorage', nextStepID: 'addSecondaryStorage', diagram: '.part.zone, .part.secondaryStorage' }), /** * Add secondary storage * @param args */ addSecondaryStorage: elems.step({ title: _l('label.add.secondary.storage'), id: 'add-secondary-storage', stateID: 'secondaryStorage', tooltipID: 'addSecondaryStorage', prevStepID: 'addSecondaryStorageIntro', nextStepID: 'launchInfo', diagram: '.part.zone, .part.secondaryStorage', form: { nfsServer: { label: 'label.nfs.server', validation: { required: true } }, path: { label: 'label.path', validation: { required: true } } } }), /** * Pre-launch text */ launchInfo: function(args) { var $intro = $('
').addClass('intro'); var $title = $('
').addClass('title') .html(_l('label.congratulations')); var $subtitle = $('
').addClass('subtitle') .html(_l('label.installWizard.click.launch')); var $continue = elems.nextButton(_l('label.launch')); var $prev = elems.prevButton(_l('label.back')); $continue.click(function() { goTo('launch'); return false; }); $prev.click(function() { goTo('addSecondaryStorage'); }); showDiagram('.part.zone, .part.secondaryStorage'); return $intro.append( $title, $subtitle, $prev, $continue ); }, /** * Pre-launch test -- after error correction */ launchInfoError: function(args) { var $intro = $('
').addClass('intro'); var $title = $('
').addClass('title') .html(_l('label.corrections.saved')); var $subtitle = $('
').addClass('subtitle') .html(_l('message.installWizard.click.retry')); var $continue = elems.nextButton(_l('label.launch')); $continue.click(function() { goTo('launch'); return false; }); showDiagram('.part.zone, .part.secondaryStorage'); return $intro.append( $title, $subtitle, $continue ); }, /** * Initiates launch tasks */ launch: function(args) { var $intro = $('
').addClass('intro'); var $title = $('
').addClass('title') .html(_l('message.installWizard.now.building')); var $subtitle = $('
').addClass('subtitle'); showDiagram('.part.loading'); $intro.append( $title, $subtitle ); cloudStack.installWizard.action({ data: state, startFn: launchStart, response: { message: function(msg) { var $li = $('
  • ').html(_l(msg)); $subtitle.append($li); $li.siblings().addClass('complete'); }, success: function() { goTo('complete'); }, error: function(stepID, message, callback) { launchStart = callback; $subtitle.find('li:last').addClass('error'); $subtitle.append( $('

    ').html( _l('error.installWizard.message') + ':
    ' + message ), $('

    ').addClass('button').append( $('').html(_l('label.back')) ).click(function() { goTo(stepID, null, null, { nextStep: 'launchInfoError' }); }) ); } } }); return $intro; }, complete: function(args) { var $intro = $('
    ').addClass('intro'); var $title = $('
    ').addClass('title') .html(_l('message.setup.successful')); var $subtitle = $('
    ').addClass('subtitle') .html(_l('label.may.continue')); var $continue = elems.nextButton(_l('label.launch')); showDiagram(''); $continue.click(function() { $installWizard.fadeOut(function() { complete(); }); }); return $intro.append( $title, $subtitle, $continue ); } }; var initialStep = steps.intro().addClass('step'); showDiagram(''); $('html body').addClass('install-wizard'); $installWizard.append( elems.header(), elems.body().append(initialStep), $diagramParts ).appendTo($container); }; cloudStack.uiCustom.installWizard = installWizard; }(jQuery, cloudStack));