Created
November 6, 2012 17:41
-
-
Save samshull/4026270 to your computer and use it in GitHub Desktop.
ranges that support unicode characters and dates (sort of)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment