(function() { var StopIterator = this.StopIterator || function() { Error.apply(this, arguments); }; function Range(/*[start, ] stop [, step [, exclusive]]*/) { var args = Array.prototype.slice.call(arguments, 0); if (!(this instanceof Range)) return Range.apply(new Range(1), args); switch(args.length) { case 0: throw new Error('Cannot create a Range without at least a maximum limit'); case 1: args[1] = args[0]; args[0] = 0; case 2: args[2] = 1; args[3] = 1; case 3: args[4] = false; case 4: if (isNaN(parseFloat(args[2]))) { throw new Error('The third argument of Range must be a number instead found ' + typeof(args[2])); } default: this._start = args[0]; this._stop = args[1]; this._step = Math.abs(parseFloat(args[2])); this._exclusive = !!arguments[3]; } // check that the range values are valid Range.succ(this._start); Range.succ(this._stop); return this; } Range.succ = function(object, step) { step = step || 1; switch(Object.prototype.toString.call(object)) { case '[object Date]': return new Date(object.valueOf() + step); case '[object Number]': return object + step; case '[object String]': return String.fromCharCode(object.charCodeAt(0) + step); default: if ("next" in object) { return object.next(step); } throw new Error('Bad value for range'); } }; Range.equal = function(x, y) { if (x === null || x === undefined || y === null || y === undefined) { return x === y; } switch(Object.prototype.toString.call(x)) { case '[object Array]': case '[object Object]': if (Object.prototype.toString.call(y) === Object.prototype.toString.call(x)) { for (var p in x) { if (!(p in y) || !this.equal(x[p], y[p])) { return false; } } return true; } return false; case '[object Date]': return x.valueOf() === y.valueOf(); default: return x.valueOf() === y.valueOf(); } }; Range.prototype.toArray = function() { return this._array = this._array || this.getArray(); }; Range.prototype.getArray = function(step, exclusive) { var start = this._start, stop = this._stop, result = [], hold = null; step = Math.abs(step || this._step); exclusive = exclusive === undefined ? this._exclusive : !!exclusive; if (stop < start) { hold = start; start = stop; stop = hold; } while (start < stop) { result[result.length] = start; start = Range.succ(start, step); } if (!exclusive) { result[result.length] = stop; } return hold !== null ? result.reverse() : result; }; Range.prototype.indexOf = function(object, offset) { var array = this.toArray(); offset = Math.floor(offset || 0); for (var length = array.length; offset < length; ++offset) { if (Range.equal(array[offset], object)) { return offset; } } return -1; }; Range.prototype.includes = function(object) { return this.indexOf(object) > -1; }; Range.prototype.covers = function(object) { return this.min() <= object && this.max() >= object; }; Range.prototype.first = function() { return this._start; }; Range.prototype.last = function() { return this._stop; }; Range.prototype.end = function() { if (!this._exclusive) { return this._stop; } var array = this.toArray(); return array[array.length - 1]; }; Range.prototype.max = function() { return this.start < this._stop ? this._stop : null; }; Range.prototype.min = function() { return this.start < this._stop ? this._start : null; }; Range.prototype.step = function(step, callback) { var iterator = this.__iterator__(step), next; if (!callback) { return iterator; } try { while (next = iterator.next()) { if (callback.call(this, next) === false) { break; } } } catch(e) { if (!(e instanceof StopIterator)) { throw e; } } return this; }; Range.prototype.each = function(callback) { return this.step(null, callback); }; Range.prototype.__iterator__ = function(step) { return new RangeIterator(this, step); } function RangeIterator(range, step) { this._range = range; this._array = range.getArray(step); this._index = 0; } RangeIterator.prototype.next = function() { if (this._index < this._array.length) { return this._array[this._index++]; } throw new StopIterator(); }; this.Range = Range; }).call(this);