Created
June 8, 2017 15:49
-
-
Save runfalk/11de6afc246c184216edb80320005340 to your computer and use it in GitHub Desktop.
Old interval implementation for JavaScript
This file contains hidden or 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 InvalidSetException(message) { | |
this.message = message | |
} | |
function DateRange(lower, upper) { | |
this.empty = false; | |
this.lower = lower; | |
this.upper = upper; | |
if (this.lower) { | |
this.lower = this.lower.clone(); | |
} | |
if (this.upper) { | |
this.upper = this.upper.clone(); | |
} | |
this.normalize(); | |
} | |
function IntRange(lower, upper) { | |
this.empty = false; | |
if (lower instanceof Array && typeof(upper) === "undefined") { | |
// Assume JSON serialization | |
this.lower = lower[0]; | |
this.upper = lower[1]; | |
} else { | |
this.lower = lower; | |
this.upper = upper; | |
} | |
this.normalize(); | |
} | |
function complete_range_prototype(range_type) { | |
range_type.emptyRange = function () { | |
var out = new range_type(); | |
out.empty = true; | |
return out; | |
} | |
range_type.prototype.normalize = function () { | |
if (this.lower >= this.upper) { | |
this.empty = true; | |
this.lower = undefined; | |
this.upper = undefined; | |
} | |
} | |
range_type.prototype.clone = function () { | |
return (this.empty ? | |
new range_type.emptyRange() : | |
new range_type( | |
this.lower.clone ? this.lower.clone() : this.lower, | |
this.upper.clone ? this.upper.clone() : this.upper)); | |
} | |
range_type.prototype.isRange = function (other) { | |
return ( | |
typeof other.empty == "boolean" && | |
typeof other.lower == "object" && | |
typeof other.upper == "object"); | |
} | |
range_type.prototype.equal = function (other) { | |
return ( | |
this.isRange(other) && | |
this.empty == other.empty && | |
this._equal(this.lower, other.lower) && | |
this._equal(this.upper, other.upper) | |
); | |
} | |
range_type.prototype.lowerInf = function () { | |
return typeof this.lower == "undefined"; | |
} | |
range_type.prototype.upperInf = function () { | |
return typeof this.upper == "undefined" | |
} | |
range_type.prototype.valueOf = function () { | |
return [this.lower.valueOf(), this.upper.valueOf()]; | |
} | |
range_type.prototype._equal = function (a, b) { | |
if (typeof a == "undefined" || typeof b == "undefined") { | |
return a == b; | |
} else { | |
return a.valueOf() === b.valueOf(); | |
} | |
} | |
range_type.prototype._min = function (a, b) { | |
if (typeof a == "undefined") { | |
return a; | |
} else if (typeof b == "undefined") { | |
return b; | |
} else { | |
return a.valueOf() < b.valueOf() ? a : b; | |
} | |
} | |
range_type.prototype._max = function (a, b) { | |
if (typeof a == "undefined") { | |
return a; | |
} else if (typeof b == "undefined") { | |
return b; | |
} else { | |
return a.valueOf() > b.valueOf() ? a : b; | |
} | |
} | |
range_type.prototype.intersection = function (other) { | |
if (this.empty || other.empty) { | |
return this.emptyRange() | |
} else { | |
return new range_type( | |
this._max(this.lower, other.lower), | |
this._min(this.upper, other.upper)); | |
} | |
} | |
range_type.prototype.union = function (other) { | |
if ( | |
this.overlap(other) || | |
this._equal(this.lower, other.upper) || | |
this._equal(this.upper, other.lower)) { | |
return new range_type( | |
this._min(this.lower, other.lower), | |
this._max(this.upper, other.upper)); | |
} else { | |
throw new InvalidSetException("These sets can't be united.") | |
} | |
} | |
range_type.prototype.difference = function (other) { | |
if ( | |
this.contains(other) && | |
this._equal(this.lower, other.lower) && | |
this._equal(this.upper, other.upper)) { | |
throw new InvalidSetException( | |
"Taking the difference of these sets would result in a discontinious set"); | |
} else if ( | |
!this.overlap(other) || | |
other.contains(this) || | |
this.empty) { | |
return range_type.emptyRange(); | |
} else if (other.empty) { | |
return this; | |
} else { | |
var overlap = this.intersection(other); | |
if (this._equal(this.lower, overlap.lower)) { | |
return new range_type(overlap.upper, this.upper); | |
} else { | |
return new range_type(this.lower, overlap.lower); | |
} | |
} | |
} | |
range_type.prototype.overlap = function (other) { | |
return !this.intersection(other).empty; | |
} | |
range_type.prototype.contains = function (other) { | |
if (this.isRange(other)) { | |
var intersection = this.intersection(other); | |
if (intersection.empty) { | |
return false; | |
} | |
intersection = intersection.valueOf(); | |
return intersection[0] == other.valueOf()[0] && | |
intersection[1] == other.valueOf()[1]; | |
} else if (other.empty || this.lowerInf() && this.upperInf()) { | |
return true | |
} else if (this.lowerInf()) { | |
return other < this.upper; | |
} else if (this.upperInf()) { | |
return other >= this.lower; | |
} else { | |
return other >= this.lower && other < this.upper; | |
} | |
} | |
} | |
complete_range_prototype(DateRange); | |
complete_range_prototype(IntRange); | |
DateRange.prototype.size = function () { | |
if (this.lowerInf() || this.upperInf()) { | |
return false; | |
} | |
return this.empty ? 0 : this.upper.diff(this.lower, "seconds"); | |
} | |
DateRange.prototype.toJSON = function () { | |
return [ | |
this.lower.format("YYYY-MM-DD HH:mm:ss"), | |
this.upper.format("YYYY-MM-DD HH:mm:ss") | |
]; | |
} | |
DateRange.prototype.asString = function (long, sup) { | |
if (this.empty) { | |
return ""; | |
} | |
var string = format_moment(this.lower, long, false, !sup) + " - "; | |
if (this.lower.dayOfYear() != this.upper.dayOfYear()) { | |
return string + format_moment(this.upper, long, false, !sup); | |
} else { | |
return string + format_moment(this.upper, false, false, !sup); | |
} | |
} | |
IntRange.prototype.asDateRange = function (reference_moment) { | |
var date_start = reference_moment.clone().add("s", this.lower); | |
var date_end = reference_moment.clone().add("s", this.upper); | |
return new DateRange(date_start, date_end); | |
} | |
IntRange.prototype.asTimeSpan = function () { | |
return ( | |
pad(Math.floor(this.lower / 3600) % 24, 2) + ":" + | |
pad(Math.floor((this.lower % 3600) / 60), 2) + | |
" - " + | |
pad(Math.floor(this.upper / 3600) % 24, 2) + ":" + | |
pad(Math.floor((this.upper % 3600) / 60), 2)); | |
} | |
/** | |
* The accepted formats are HH, HHMM, HH MM, HH:MM, (HH,MM), H, HMM, H:MM, H MM, (H,MM) | |
*/ | |
IntRange.parseTimeSpan = function (input_string) { | |
// Remove all whitespace | |
input_string = input_string.replace(/\s+/g, ""); | |
// There must be a "-" in it | |
if (input_string.indexOf("-") === -1) { | |
return false; | |
} | |
var start = input_string.split("-")[0]; | |
var end = input_string.split("-")[1]; | |
var format = /^([01]?[0-9]|2[0-4])[:,\.]?([0-5][0-9])?$/; | |
var parsed_start = start.match(format); | |
var parsed_end = end.match(format); | |
if (!parsed_start || !parsed_end) { | |
return false; | |
} | |
var start = (parsed_start[1] || 0) * 3600 + (parsed_start[2] || 0) * 60; | |
var end = (parsed_end[1] || 0) * 3600 + (parsed_end[2] || 0) * 60; | |
if (end < start) { | |
end += 86400; | |
} | |
return new IntRange(start, end); | |
} | |
IntRange.prototype.toJSON = function () { | |
return [this.lower, this.upper]; | |
} | |
function DateRangeList(spans) { | |
this._spans = [] | |
if (spans) { | |
for (var i = 0; i < spans.length; i++) { | |
this.add(spans[i]); | |
} | |
} | |
} | |
function IntRangeList(spans) { | |
this._spans = [] | |
if (spans) { | |
for (var i = 0; i < spans.length; i++) { | |
this.add(spans[i]); | |
} | |
} | |
} | |
function complete_range_list_prototype(range_list_type, range_type) { | |
range_list_type.prototype.lower = function () { | |
return this.isEmpty() ? | |
"undefined" : | |
(this._spans[0].lower.clone ? | |
this._spans[0].lower.clone() : this._spans[0].lower); | |
} | |
range_list_type.prototype.upper = function () { | |
return this.isEmpty() ? | |
"undefined" : | |
(this._spans[this._spans.length - 1].upper.clone ? | |
this._spans[this._spans.length - 1].upper.clone() : | |
this._spans[this._spans.length - 1].upper); | |
} | |
range_list_type.prototype.add = function (span) { | |
// Assume serialized data | |
if (span instanceof Array) { | |
this._spans.push(new IntRange(span)); | |
} else { | |
this._spans.push(span); | |
} | |
this.sort(); | |
} | |
range_list_type.prototype.remove = function (span) { | |
for (var i = 0; i < this._spans.length; i++) { | |
if (this._spans[i].equal(span)) { | |
return this._spans.splice(i, 1); | |
} | |
} | |
} | |
range_list_type.prototype.sort = function () { | |
this._spans.sort(function (a, b) { | |
if (a < b) { | |
return -1; | |
} | |
if (a > b) { | |
return 1; | |
} | |
return 0 | |
}) | |
} | |
range_list_type.prototype.overlap = function (other) { | |
var overlap = false; | |
this.each(function (span_a) { | |
other.each(function (span_b) { | |
overlap = overlap || span_a.overlap(span_b); | |
}); | |
}); | |
return overlap; | |
} | |
range_list_type.prototype.intersection = function (other) { | |
var intersection_list = new range_list_type(); | |
this.each(function (span) { | |
var intersection = span.clone().intersection(other.clone()); | |
if (!intersection.empty) { | |
intersection_list.add(intersection); | |
} | |
}); | |
return intersection_list; | |
} | |
range_list_type.prototype.clone = function () { | |
var clone = new range_list_type(); | |
_.each(this._spans, function (span) { | |
clone.add(span.clone()); | |
}); | |
return clone; | |
} | |
range_list_type.prototype.each = function (loop_function) { | |
_.each(this._spans, function (span) { | |
loop_function(span); | |
}); | |
} | |
range_list_type.prototype.isEmpty = function () { | |
return !this._spans.length; | |
} | |
range_list_type.prototype.asArray = function () { | |
return this.clone()._spans; | |
} | |
range_list_type.prototype.span = function () { | |
return new range_type(this.lower(), this.upper()); | |
} | |
range_list_type.prototype.toJSON = function () { | |
var out = []; | |
for (var i = 0; i < this._spans.length; i++) { | |
out.push(this._spans[i].toJSON()); | |
} | |
return out; | |
} | |
range_list_type.prototype.equal = function (other) { | |
if ( | |
!other._spans || | |
this._spans.length != other._spans.length) { | |
return false; | |
} | |
for (var i = 0; i < this._spans.length; i++) { | |
if (!this._spans[i].equal(other._spans[i])) { | |
return false; | |
} | |
} | |
return true; | |
} | |
range_list_type.prototype.union = function (other) { | |
union = new range_list_type(); | |
this.each(function (span) { | |
union.add(span) | |
}); | |
other.each(function (span) { | |
union.add(span) | |
}); | |
// Merge all overlaping spans in spanlist | |
var i = 0; | |
while (i < union._spans.length) { | |
for (var j = 0; j < union._spans.length; j++) { | |
if (i != j && union._spans[i].overlap(union._spans[j])) { | |
var span_a = union._spans[i]; | |
var span_b = union._spans[j]; | |
union.remove(span_a); | |
union.remove(span_b); | |
union.add(span_a.union(span_b)); | |
i = -1; | |
break; | |
} | |
} | |
i++; | |
} | |
return union; | |
} | |
range_list_type.prototype.size = function () { | |
var sum = 0 | |
_.each(this._spans, function(span) { | |
sum += span.size(); | |
}); | |
return sum; | |
} | |
} | |
complete_range_list_prototype(DateRangeList, DateRange); | |
complete_range_list_prototype(IntRangeList, IntRange); | |
function format_date_range(date_range, long_date, span, text) { | |
var html_string = format_moment(date_range.lower, long_date, span, text) + " - "; | |
if (date_range.lower.dayOfYear() != date_range.upper.dayOfYear()) { | |
html_string += format_moment(date_range.upper, true, span, text); | |
} else { | |
html_string += format_moment(date_range.upper, false, span, text) | |
} | |
return html_string; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment