Source: SoupPermutations.js

/**
 * Creates a new SoupPermutations object
 *
 * @constructor
 * @param {array} set	The set to permute
 */
function SoupPermutations(set) {
	// Always working reference to this
	var self = this;

	// Verify that the set is an array
	if (!(set instanceof Array))
		throw "Given set is not an array";
	if (set.length < 1)
		throw "Need at least one item to permute. " + set.length + " given";

	// Copy the set
	var set = (function() {
		var copy = [];
		for (var i = 0; i < set.length; i++) {
			copy.push(set[i]);
		}
		return copy;
	})();
	var setItems = set.length;

	// Place to hold the current order of the objects
	var currentPermutation = 0;
	var possiblePermutations = (function() {
		var product = 1;
		for (var i = 1; i <= set.length; i++) {
			product *= i;
		}
		return product;
	})();
	Object.defineProperties(this, {
		/**
		 * Number of the current permutation
		 *
		 * @return {number}
		 * @memberof SoupPermutations
		 * @instance
		 */
		currentPermutationNumber: {get: function() {return currentPermutation;}},
		/**
		 * Number of possible permutations of this set
		 *
		 * @return {number}
		 * @memberof SoupPermutations
		 * @instance
		 */
		possiblePermutations: {get: function() {return possiblePermutations;}}
	});
	var currentPermutationArray = [];
	for (var i = setItems-1; i >= 0; i--) {
		currentPermutationArray.push(i);
	}

	/**
	 * Checks if more permutations can be made from the set
	 *
	 * @return {boolean}	True if more permutations can be made, false otherwise
	 */
	this.hasNext = function() {
		var hasNext = self.currentPermutationNumber != self.possiblePermutations;
		if (!hasNext) {
			self.next = function() {
				throw "All possible permutations have been generated";
			};
		}
		return hasNext;
	};

	/**
	 * Calculate and return the next permutation. Will throw an exception if called when {@SoupPermutations#hasNext} returns false
	 *
	 * @return {Array}	An array with the next permutation of the set
	 */
	this.next = function() {
		currentPermutation++;

		// Find first item from the right which is greater than the one on it's left
		var i;
		for (i = setItems-2; i >= 0; i--) {
			if (currentPermutationArray[i] < currentPermutationArray[i+1]) {
				break;
			}
		}

		// Find first item from the right which is greater than the one on position i
		var j;
		for (j = setItems-1; j >= 0; j--) {
			if (currentPermutationArray[j] > currentPermutationArray[i]) {
				// Swap the items on i and j
				var tmp = currentPermutationArray[j];
				currentPermutationArray[j] = currentPermutationArray[i];
				currentPermutationArray[i] = tmp;
				break;
			}
		}

		// Reverse the items from i+1 to the end
		i = i+1;
		j = setItems-1;
		while (i < j) {
			var tmp = currentPermutationArray[i];
			currentPermutationArray[i] = currentPermutationArray[j];
			currentPermutationArray[j] = tmp;
			i++;
			j--;
		}

		return self.current();
	};

	/**
	 * Gets a copy of the current permutation
	 *
	 * @return {Array}	An array with the current permutation of the set
	 */
	this.current = function() {
		var perm = [];
		for (var i = 0; i < currentPermutationArray.length; i++) {
			perm.push(set[currentPermutationArray[i]]);
		}
		return perm;
	};
}