'use strict';
var app = angular.module('app', []);
app.controller('MainCtrl', [ '$scope', function($scope) {

	$scope.getProgressClass = function(progress, result) {
		if (progress == 100) {
			return "progress-bar-success";
		} else if (progress < 100 && result != null) {
			return "progress-bar-info"
		} else {
			return "progress-bar-danger"
		}
	}
} ]);
app.service('PromiseService', [ '$q', '$timeout', function($q, $timeout) {
	var service = this;

	this.getPromise = function(delay) {
		var deferred = $q.defer();

		$timeout(function() {
			deferred.notify(delay);
		}, 0);

		$timeout(function() {
			deferred.notify(delay);
			deferred.resolve("Promise finished with success");
		}, delay || 0);

		return deferred.promise;
	};

	this.getRecursivePromise = function(number) {
		var deferred = $q.defer();

		$timeout(function() {
			deferred.notify(number);
		}, 0);

		var delay = Math.floor((Math.random() * 10) + 1) * 200;

		$timeout(function() {
			if (number > 1) {
				deferred.resolve(service.getRecursivePromise(--number));
			} else if (number == 1) {
				deferred.resolve("All promises finished with success");
			} else {
				deferred.reject("An error occured !");
			}
		}, delay);

		return deferred.promise;
	};

	this.getCombinedPromises = function(promises) {
		var deferred = $q.defer();

		angular.forEach(promises, function(value, key) {
			value.then(null, null, function(value) {
				$timeout(function() {
					deferred.notify(value);
				}, 0);
			});
		});

		$q.all(promises).then(function(value) {
			deferred.resolve(value)
		}, function(reason) {
			deferred.reject(reason);
		});

		return deferred.promise;
	};

	return service;
} ]);
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" />
<style>
body {
	padding: 50px;
}

form {
	margin-top: 0 !important;
	padding: 0 !important;
}

form input {
	width: 100% !important;
}

.alert {
	padding: 5px 15px 5px 15px;
}

.progress {
	height: 30px;
	padding: 0;
}

.progress span {
	font-size: 2em;
	line-height: 30px;
}
</style>
</head>
<body ng-app="app" ng-controller="MainCtrl">
	<h3>Example 1 : Simple promise</h3>
	<div class="row" ng-controller="ExampleOneCtrl">
		<div class="col-xs-2">
			<form class="navbar-form navbar-left" ng-submit="generatePromise(delay)" class="form-inline" role="form">
				<div class="form-group">
					<label class="sr-only" for="delay">Delay:</label> <input ng-model="delay" id="delay" type="number" class="form-control" placeholder="Delay of the promise" required="required">
				</div>
				<button ng-disabled="!(!promise || promise.progress == 100)" type="submit" class="btn btn-default">Submit</button>
			</form>
		</div>
		<div class="col-xs-10 progress">
			<div ng-class="getProgressClass(promise.progress, promise.result)" class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="{{promise.progress}}" aria-valuemin="0"
				aria-valuemax="100" style="width: {{promise.progress}}%">
				<span>{{promise.progress}} %</span>
			</div>
		</div>
		<div ng-if="promise.result != null && promise.progress == 100" class="col-xs-12 alert alert-success" role="alert">{{promise.result}}</div>
		<div ng-if="promise.result != null && !(promise.progress == 100)" class="col-xs-12 alert alert-info" role="alert">{{promise.result}}</div>
	</div>
	<hr />

	<h3>Example 2 : Chained promises</h3>
	<div ng-controller="ExampleTwoCtrl">
		<form ng-submit="generateChainedPromises(promises)" role="form">
			<button ng-disabled="promises.length == 0 || promise.progress < 100" type="submit" class="btn btn-default">Submit</button>
			<button ng-disabled="promise.progress == 100" ng-click="add()" class="btn btn-default">Add</button>
			<button ng-disabled="promise && promise.progress != 100" ng-click="reset()" class="btn btn-default" type="reset">Reset</button>
			<br /> <br />
			<div class="row" ng-repeat="promise in promises" ng-controller="ExampleTwoCtrl">
				<div class="col-xs-2 form-group">
					<label class="sr-only" for="delay">Delay:</label> <input ng-model="promise.delay" id="delay" type="number" class="form-control" placeholder="Delay of the promise" required="required">
				</div>
				<div class="col-xs-10 progress">
					<div ng-class="getProgressClass(promise.progress, promise.result)" class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="{{promise.progress}}" aria-valuemin="0"
						aria-valuemax="100" style="width: {{promise.progress}}%">
						<span>{{promise.progress}} %</span>
					</div>
				</div>
				<div ng-if="promise.result != null && promise.progress == 100" class="col-xs-12 alert alert-success" role="alert">{{promise.result}}</div>
				<div ng-if="promise.result != null && !(promise.progress == 100)" class="col-xs-12 alert alert-info" role="alert">{{promise.result}}</div>
			</div>
		</form>
		<div class="progress">
			<div ng-class="getProgressClass(promise.progress, promise.result)" class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="{{promise.progress}}" aria-valuemin="0"
				aria-valuemax="100" style="width: {{promise.progress}}%">
				<span>{{promise.progress}} %</span>
			</div>
		</div>
		<div ng-if="promise.result != null && promise.progress == 100" class="alert alert-success" role="alert">{{promise.result}}</div>
		<div ng-if="promise.result != null && !(promise.progress == 100)" class="alert alert-info" role="alert">{{promise.result}}</div>
	</div>
	<hr />

	<h3>Example 3 : Recursive promise (N times)</h3>
	<div class="row" ng-controller="ExampleThreeCtrl">
		<div class="col-xs-2">
			<form class="navbar-form navbar-left" ng-submit="generateRecursivePromise(number)" class="form-inline" role="form">
				<div class="form-group">
					<label class="sr-only" for="number">Number:</label> <input ng-model="number" id="number" type="number" class="form-control" placeholder="Number of recursive calls" required="required">
				</div>
				<button ng-disabled="!(!promise || promise.progress == 100)" type="submit" class="btn btn-default">Submit</button>
			</form>
		</div>
		<div class="col-xs-10 progress">
			<div ng-class="getProgressClass(promise.progress, promise.result)" class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="{{promise.progress}}" aria-valuemin="0"
				aria-valuemax="100" style="width: {{promise.progress}}%">
				<span>{{promise.progress}} %</span>
			</div>
		</div>
		<div ng-if="promise.result != null && promise.progress == 100" class="col-xs-12 alert alert-success" role="alert">{{promise.result}}</div>
		<div ng-if="promise.result != null && !(promise.progress == 100)" class="col-xs-12 alert alert-info" role="alert">{{promise.result}}</div>
	</div>
	<hr />

	<h3>Example 4 : Combined promises</h3>
	<div ng-controller="ExampleFourCtrl">
		<form ng-submit="generateCombinedPromises(promises)" role="form">
			<button ng-disabled="promises.length == 0 || promise.progress < 100" type="submit" class="btn btn-default">Submit</button>
			<button ng-disabled="promise" ng-click="add()" class="btn btn-default">Add</button>
			<button ng-disabled="promise && promise.progress != 100" ng-click="reset()" class="btn btn-default" type="reset">Reset</button>
			<br /> <br />
			<div class="row" ng-repeat="promise in promises" ng-controller="ExampleTwoCtrl">
				<div class="col-xs-2 form-group">
					<label class="sr-only" for="delay">Delay:</label> <input ng-model="promise.delay" id="delay" type="number" class="form-control" placeholder="Delay of the promise" required="required">
				</div>
				<div class="col-xs-10 progress">
					<div ng-class="getProgressClass(promise.progress, promise.result)" class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="{{promise.progress}}" aria-valuemin="0"
						aria-valuemax="100" style="width: {{promise.progress}}%">
						<span>{{promise.progress}} %</span>
					</div>
				</div>
				<div ng-if="promise.result != null && promise.progress == 100" class="col-xs-12 alert alert-success" role="alert">{{promise.result}}</div>
				<div ng-if="promise.result != null && !(promise.progress == 100)" class="col-xs-12 alert alert-info" role="alert">{{promise.result}}</div>
			</div>
		</form>
		<div class="progress">
			<div ng-class="getProgressClass(promise.progress, promise.result)" class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="{{promise.progress}}" aria-valuemin="0"
				aria-valuemax="100" style="width: {{promise.progress}}%">
				<span>{{promise.progress}} %</span>
			</div>
		</div>
		<div ng-if="promise.result != null && promise.progress == 100" class="alert alert-success" role="alert">{{promise.result}}</div>
		<div ng-if="promise.result != null && !(promise.progress == 100)" class="alert alert-info" role="alert">{{promise.result}}</div>
	</div>
	<hr />

	<h3>Example 5 : Combined recursive promises (N times)</h3>
	<div ng-controller="ExampleFiveCtrl">
		<form ng-submit="generateCombinedPromises(promises)" role="form">
			<button ng-disabled="promises.length == 0 || promise.progress < 100" type="submit" class="btn btn-default">Submit</button>
			<button ng-disabled="promise" ng-click="add()" class="btn btn-default">Add</button>
			<button ng-disabled="promise && promise.progress != 100" ng-click="reset()" class="btn btn-default" type="reset">Reset</button>
			<br /> <br />
			<div class="row" ng-repeat="promise in promises" ng-controller="ExampleTwoCtrl">
				<div class="col-xs-2 form-group">
					<label class="sr-only" for="number">Number:</label> <input ng-model="promise.number" id="number" type="number" class="form-control" placeholder="Number of recursive calls" required="required">
				</div>
				<div class="col-xs-10 progress">
					<div ng-class="getProgressClass(promise.progress, promise.result)" class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="{{promise.progress}}" aria-valuemin="0"
						aria-valuemax="100" style="width: {{promise.progress}}%">
						<span>{{promise.progress}} %</span>
					</div>
				</div>
				<div ng-if="promise.result != null && promise.progress == 100" class="col-xs-12 alert alert-success" role="alert">{{promise.result}}</div>
				<div ng-if="promise.result != null && !(promise.progress == 100)" class="col-xs-12 alert alert-info" role="alert">{{promise.result}}</div>
			</div>
		</form>
		<div class="progress">
			<div ng-class="getProgressClass(promise.progress, promise.result)" class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="{{promise.progress}}" aria-valuemin="0"
				aria-valuemax="100" style="width: {{promise.progress}}%">
				<span>{{promise.progress}} %</span>
			</div>
		</div>
		<div ng-if="promise.result != null && promise.progress == 100" class="alert alert-success" role="alert">{{promise.result}}</div>
		<div ng-if="promise.result != null && !(promise.progress == 100)" class="alert alert-info" role="alert">{{promise.result}}</div>
	</div>

	<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular.min.js"></script>
	<script src="app.js"></script>
	<script src="PromiseService.js"></script>
	<script src="MainCtrl.js"></script>
	<script src="ExampleOneCtrl.js"></script>
	<script src="ExampleTwoCtrl.js"></script>
	<script src="ExampleThreeCtrl.js"></script>
	<script src="ExampleFourCtrl.js"></script>
	<script src="ExampleFiveCtrl.js"></script>
</body>
</html>
app.controller('ExampleTwoCtrl', [ '$scope', 'PromiseService', function($scope, PromiseService) {

	$scope.promises = [];

	$scope.add = function() {
		$scope.promises.push({});
	}

	$scope.reset = function() {
		$scope.promise = null;
		$scope.promises = [];
	};

	$scope.generateChainedPromises = function(futurePromises) {
		$scope.promise = {
			result : []
		};

		$scope.startNextPromise(0).then(function(value) {
			$scope.promise.progress = 100;
		});
	};

	$scope.startNextPromise = function(index) {
		return PromiseService.getPromise(($scope.promises[index].delay || 0) * 1000).then(function(value) {
			$scope.promises[index].progress = 100;
			$scope.promises[index].result = value;
			$scope.promise.result.push(value);

			if (index < $scope.promises.length - 1) {
				return $scope.startNextPromise(++index);
			} else {
				return value;
			}
		}, null, function(value) {
			$scope.promises[index].result = (value / 1000) + " seconds to process...";
		});
	};
} ]);
app.controller('ExampleThreeCtrl', [ '$scope', 'PromiseService', function($scope, PromiseService) {

	$scope.generateRecursivePromise = function(number) {
		$scope.promise = PromiseService.getRecursivePromise(number || 0).then(function(value) {
			$scope.promise.result = value;
			$scope.promise.progress = 100;
		}, function(reason) {
			$scope.promise.result = reason;
		}, function(value) {
			$scope.promise.result = value + " promises left...";
			$scope.promise.progress = ((number - value) * (100 / number)) || 0;
		});
	};
} ]);
app.controller('ExampleOneCtrl', [ '$scope', 'PromiseService', function($scope, PromiseService) {

	$scope.generatePromise = function(delay) {
		$scope.promise = PromiseService.getPromise(delay * 1000).then(function(value) {
			$scope.promise.result = value;
			$scope.promise.progress = 100;
		}, function(reason) {
			$scope.promise.result = reason;
		}, function(value) {
			$scope.promise.result = value;
		});
	};
} ]);
app.controller('ExampleFourCtrl', [ '$scope', 'PromiseService', function($scope, PromiseService) {

	$scope.promises = [];

	$scope.add = function() {
		$scope.promises.push({});
	}

	$scope.reset = function() {
		$scope.promise = null;
		$scope.promises = [];
	};

	$scope.generateCombinedPromises = function(futurePromises) {
		$scope.promise = {};

		angular.forEach(futurePromises, function(value, key) {
			$scope.promises[key] = PromiseService.getPromise((value.delay || 0) * 1000);

			$scope.promises[key].then(function(value) {
				$scope.promises[key].result = value;
				$scope.promises[key].progress = 100;
			}, function(reason) {
				$scope.promises[key].result = reason;
			}, function(value) {
				$scope.promises[key].result = (value / 1000) + " seconds to process...";
			});
		});

		$scope.promise.result = [];
		PromiseService.getCombinedPromises($scope.promises).then(function(value) {
			$scope.promise.result = value;
			$scope.promise.progress = 100;
		}, function(reason) {
			$scope.promise.result = reason;
		}, function(value) {
			var progress = 0;
			angular.forEach($scope.promises, function(value, key) {
				progress += (value.progress || 0);
			});
			$scope.promise.progress = progress / $scope.promises.length;
			$scope.promise.result.push(value);
		});
	};
} ]);
app.controller('ExampleFiveCtrl', [ '$scope', 'PromiseService', function($scope, PromiseService) {

	$scope.promises = [];

	$scope.add = function() {
		$scope.promises.push({});
	}

	$scope.reset = function() {
		$scope.promise = null;
		$scope.promises = [];
	};

	$scope.generateCombinedPromises = function(futurePromises) {
		$scope.promise = {
			number : 0
		};

		angular.forEach(futurePromises, function(value, key) {
			$scope.promises[key] = PromiseService.getRecursivePromise(value.number || 0);
			$scope.promises[key].number = value.number;
			$scope.promise.number += value.number;

			$scope.promises[key].then(function(value) {
				$scope.promises[key].result = value;
				$scope.promises[key].progress = 100;
			}, function(reason) {
				$scope.promises[key].result = reason;
			}, function(value) {
				$scope.promises[key].result = value + " promises left...";
				$scope.promises[key].progress = (($scope.promises[key].number - value) * (100 / $scope.promises[key].number)) || 0;
			});
		});

		$scope.promise.result = [];
		PromiseService.getCombinedPromises($scope.promises).then(function(value) {
			$scope.promise.result = value;
			$scope.promise.progress = 100;
		}, function(reason) {
			$scope.promise.result = reason;
		}, function(value) {
			var progress = 0;
			angular.forEach($scope.promises, function(value, key) {
				progress += (value.progress || 0);
			});
			$scope.promise.progress = progress / $scope.promises.length;
			$scope.promise.result.push(value);
		});
	};
} ]);