<!DOCTYPE html>
<html ng-app="app">

  <head>
    <link data-require="bootstrap-css@3.1.*" data-semver="3.1.1" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
    <link data-require="font-awesome@*" data-semver="4.2.0" rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css" />
    <script data-require="angular.js@*" data-semver="1.3.7" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.7/angular.js"></script>
    <script data-require="ui-bootstrap@*" data-semver="0.12.0" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.12.0.min.js"></script>
    <script data-require="angular-animate@*" data-semver="1.3.5" src="https://code.angularjs.org/1.3.5/angular-animate.js"></script>
    <link rel="stylesheet" href="ngWizard.css" />
    <script src="app.js"></script>
    <script src="ngWizard.js"></script>
  </head>

  <body ng-controller="wizard">
    <h1>ngWizard Demo</h1>
    <wizard current-step-number="currentStepNumber" submit="submit()">
  <wizard-step title="step 1" entered="stepEntered()">
    <p>step 1</p>
    <input type="text" ng-model="requiredText" required placeholder="Required Field"/>
  </wizard-step>
  <wizard-step title="step 2" required-step-number="0" entered="stepEntered()">
    <p>step 2</p>
    <input type="email" ng-model="requiredText" required placeholder="Required Email Address"/>
  </wizard-step>
  <wizard-step ng-repeat="step in dynamicSteps" required-step-number="{{$index + 1}}" title="{{step}}">
    <p>{{step}}</p>
    <input type="text" required ng-model="localScope"/>
  </wizard-step>
</wizard>
<br/>
<br/>
<p> --- End of Wizard --- </p>
<input type="text" ng-model="newStep"/>
<button class="btn btn-primary" ng-click="addNewStep(newStep)">Add New Step</button>
  </body>

</html>
angular.module("ngWizard", [ "ui.bootstrap", "ngAnimate", "templates" ]).directive("wizard", [ "$window", "$q", function($window, $q) {
    "use strict";
    return {
        restrict: "E",
        transclude: true,
        scope: {
            currentStepNumber: "=",
            submit: "&"
        },
        templateUrl: "src/wizardTemplate.html",
        controller: function($scope) {
            $scope.currentStepNumber = $scope.currentStepNumber || 0;
            $scope.getCurrentStep = function() {
                return $scope.steps[$scope.currentStepNumber];
            };
            this.getCurrentStep = $scope.getCurrentStep;
            $scope.goToStepByReference = function(step) {
                var stepNumber = $scope.steps.indexOf(step);
                return $scope.goToStep(stepNumber);
            };
            var isValidStepNumber = function(stepNumber) {
                return stepNumber < $scope.steps.length && stepNumber >= 0;
            };
            $scope.canGoToStep = function(stepNumber) {
                if (!isValidStepNumber(stepNumber)) {
                    return false;
                }
                var newStep = $scope.steps[stepNumber];
                return $scope.getStepState(newStep) != $scope.stepStatesEnum.disabled;
            };
            $scope.goToStep = function(stepNumber) {
                if ($scope.canGoToStep(stepNumber)) {
                    $scope.currentStepNumber = stepNumber;
                    return true;
                }
                return false;
            };
            $scope.getStepState = function(step) {
                if (step.requiredStepNumber && $scope.getStepState($scope.steps[step.requiredStepNumber]) != $scope.stepStatesEnum.complete) {
                    return $scope.stepStatesEnum.disabled;
                } else if (step.stepForm.$valid) {
                    return $scope.stepStatesEnum.complete;
                } else return $scope.stepStatesEnum.ready;
            };
            $scope.stepStatesEnum = {
                disabled: 0,
                ready: 1,
                complete: 2
            };
            $scope.goToNext = function() {
                $scope.goToStep($scope.currentStepNumber + 1);
            };
            $scope.hasNext = function() {
                return $scope.steps.length > $scope.currentStepNumber + 1 && $scope.getStepState($scope.steps[$scope.currentStepNumber + 1]) != $scope.stepStatesEnum.disabled;
            };
            $scope.goToPrevious = function() {
                $scope.goToStep($scope.currentStepNumber - 1);
            };
            $scope.hasPrevious = function() {
                return $scope.currentStepNumber > 0;
            };
            $scope.getProgressPercentage = function() {
                var completeSteps = $scope.steps.filter(function(step) {
                    return $scope.getStepState(step) == $scope.stepStatesEnum.complete;
                });
                return completeSteps.length / $scope.steps.length * 100;
            };
            $scope.steps = [];
            this.registerStep = function(stepScope) {
                $scope.steps.push(stepScope);
            };
            this.unregisterStep = function(stepScope) {
                var index = $scope.steps.indexOf(stepScope);
                if (index >= 0) {
                    $scope.steps.splice(index, 1);
                }
            };
            $scope.isSubmittable = function() {
                return $scope.steps.every(function(step) {
                    return $scope.getStepState(step) == $scope.stepStatesEnum.complete;
                });
            };
            $scope.submitting = false;
            $scope.onSubmitClicked = function() {
                $scope.submitting = true;
                $q.when($scope.submit()).then(function() {
                    $scope.submitting = false;
                });
            };
            $scope.$watch("currentStepNumber", function(val, oldVal) {
                if (val != oldVal) {
                    if (!$scope.canGoToStep(val)) {
                        if (oldVal && $scope.canGoToStep(oldVal)) {
                            $scope.currentStepNumber = oldVal;
                        } else $scope.currentStepNumber = 0;
                    } else {
                        $scope.getCurrentStep().entered();
                    }
                }
            });
            $scope.$watch("steps.length", function() {
                if (!$scope.getCurrentStep()) {
                    $scope.currentStepNumber = 0;
                }
            }, true);
        }
    };
} ]).directive("wizardStep", function() {
    return {
        require: "^wizard",
        restrict: "E",
        transclude: true,
        scope: {
            title: "@",
            requiredStepNumber: "@",
            entered: "&",
            animation: "@"
        },
        template: "<ng-form name='stepForm' ng-show='isActive()' class='wizard-step animate'  ng-class='animation || \"slide\"'><ng-transclude></ng-transclude></ng-form>",
        link: function($scope, element, attrs, wizardCtrl) {
            wizardCtrl.registerStep($scope);
            $scope.isActive = function() {
                return $scope == wizardCtrl.getCurrentStep();
            };
            $scope.$on("$destroy", function() {
                wizardCtrl.unregisterStep($scope);
            });
        }
    };
});

angular.module("templates", [ "src/wizardTemplate.html" ]);

angular.module("src/wizardTemplate.html", []).run([ "$templateCache", function($templateCache) {
    $templateCache.put("src/wizardTemplate.html", '<div class="row wizard-container">\n' + '    <div class="col-md-3 col-xs-12">\n' + '        <ul class="nav nav-pills nav-stacked wizard-sidebar">\n' + '            <li tooltip="{{getProgressPercentage() | number : 2}}%">\n' + "                <progressbar value=\"getProgressPercentage()\" type=\"{{getProgressPercentage() == 100 ? 'success' : 'default'}}\"></progressbar>\n" + "            </li>\n" + '            <li ng-repeat="step in steps" ng-class="{disabled: getStepState(step) == stepStatesEnum.disabled, active: getCurrentStep() == step}"\n' + '                ng-click="goToStepByReference(step)" ng-disabled="getStepState(step) == stepStatesEnum.disabled">\n' + "                <a>\n" + '                    {{step.title}} <i class="fa fa-check" ng-show="getStepState(step) == stepStatesEnum.complete"></i>\n' + "                </a>\n" + "            </li>\n" + "        </ul>\n" + "    </div>\n" + '    <div class="col-md-9 col-xs-12 wizard-main">\n' + '        <ul class="pager">\n' + '            <li class="previous" ng-class="{disabled: !hasPrevious()}"><a href="#" ng-click="goToPrevious()"><i class="fa fa-arrow-circle-left"></i> Previous</a></li>\n' + '            <li ng-repeat="step in steps">\n' + "                <i class=\"fa\" ng-class=\"{'fa-circle-o disabled': getStepState(step) == stepStatesEnum.disabled, 'fa-circle': getStepState(step) == stepStatesEnum.complete, 'fa-circle-o': getStepState(step) == stepStatesEnum.ready, selected: getCurrentStep() == step}\"\n" + '                   ng-click="goToStepByReference(step)" tooltip="{{step.title}}"></i>\n' + "            </li>\n" + '            <li class="next" ng-class="{disabled: !hasNext()}"><a href="#" ng-click="goToNext()">Next <i class="fa fa-arrow-circle-right"></i></a></li>\n' + "        </ul>\n" + '        <div class="wizard-step-container" ng-transclude></div>\n' + "    </div>\n" + '    <div class="row">\n' + '        <div class="col-xs-12">\n' + "            <!--Don't know why, but ng-hide doesn't work here, use ng-class instead-->\n" + '            <button class="btn btn-primary btn-block submit" ng-class="{\'ng-hide\': !isSubmittable()}" ng-click="onSubmitClicked()" ng-disabled="submitting">Submit <i class="fa fa-circle-o-notch fa-spin" ng-show="submitting"></i></button>\n' + "        </div>\n" + "    </div>\n" + "</div>");
} ]);
.animate.slide.ng-hide-remove-active,
.animate.slide.ng-hide-add-active {
  -webkit-transition: 0.5s all ease;
  position: relative;
}
.animate.slide.ng-hide-remove.ng-hide-remove-active {
  transform: translateX(0);
  -ms-transform: translateX(0);
  -moz-transform: translateX(0);
  -webkit-transform: translateX(0);
  opacity: 1;
}
.animate.slide.ng-hide-add {
  display: none !important;
}
.animate.slide.ng-hide-remove {
  transform: translateX(20%);
  -ms-transform: translateX(20%);
  -moz-transform: translateX(20%);
  -webkit-transform: translateX(20%);
  opacity: 0.5;
}
.animate.fade-in.ng-hide-remove-active,
.animate.fade-in.ng-hide-add-active {
  -webkit-transition: 0.5s all ease;
  position: relative;
}
.animate.fade-in.ng-hide-remove.ng-hide-remove-active {
  opacity: 1;
}
.animate.fade-in.ng-hide-add {
  display: none !important;
}
.animate.fade-in.ng-hide-remove {
  opacity: 0;
}
.animate.zoom.ng-hide-remove-active,
.animate.zoom.ng-hide-add-active {
  -webkit-transition: 0.5s all ease;
  position: relative;
}
.animate.zoom.ng-hide-remove.ng-hide-remove-active {
  transform: translateZ(0);
  -ms-transform: translateZ(0);
  -moz-transform: translateZ(0);
  -webkit-transform: translateZ(0);
  -webkit-filter: opacity(100%);
  -moz-filter: opacity(100%);
  -o-filter: opacity(100%);
  -ms-filter: opacity(100%);
}
.animate.zoom.ng-hide-add {
  display: none !important;
}
.animate.zoom.ng-hide-remove {
  transform: translateZ(-250px);
  -ms-transform: translateZ(-250px);
  -moz-transform: translateZ(-250px);
  -webkit-transform: translateZ(-250px);
  -webkit-filter: opacity(10%);
  -moz-filter: opacity(10%);
  -o-filter: opacity(10%);
  -ms-filter: opacity(10%);
}
.wizard-container {
  background-color: #e7e7e7;
}
.wizard-container .wizard-main {
  padding: 20px;
}
.wizard-container .wizard-main .pager i {
  -webkit-transition: all 0.3s ease;
}
.wizard-container .wizard-main .pager i.selected {
  color: #38a7e2;
  -webkit-transform: translateY(-3px);
}
.wizard-container .wizard-main .pager i.disabled {
  color: gray;
  cursor: not-allowed;
}
.wizard-container .wizard-main .wizard-step-container {
  overflow-x: hidden;
  -webkit-perspective: 800px;
  -moz-perspective: 800px;
  -ms-perspective: 800px;
  perspective: 800px;
}
.wizard-container .wizard-main .wizard-step {
  background-color: white;
  padding: 15px;
  box-shadow: gray 5px 10px 30px;
  display: block;
}
.wizard-container .wizard-sidebar {
  margin-top: 65px;
  margin-bottom: 30px;
  background-color: white;
}
.wizard-container .wizard-sidebar .progress {
  margin: 0;
}
.wizard-container .wizard-sidebar li.active a {
  margin-left: 15px;
}
.wizard-container .wizard-sidebar li a {
  -ms-transition: margin 0.5s ease;
  -moz-transition: margin 0.5s ease;
  -webkit-transition: margin 0.5s ease;
  transition: margin 0.5s ease;
}
.wizard-container .btn.submit {
  margin-bottom: 5px;
}
.wizard-container .btn.submit i.ng-hide-add {
  display: none;
}
angular.module("app", ["ngWizard"])
.controller('wizard', function ($scope){
  $scope.submit = function (){
    alert("Submitted Wizard!");
  }
  $scope.addNewStep = function (newStep){
    $scope.dynamicSteps.push(newStep);
  }
  
  $scope.dynamicSteps = ["step 3"];
  $scope.dynamicRequiredText = {};
});