diff --git a/climbgrades/climbgrades.js b/climbgrades/climbgrades.js index ce9a90f..f60b0af 100644 --- a/climbgrades/climbgrades.js +++ b/climbgrades/climbgrades.js @@ -78,6 +78,16 @@ var ClimbGrades = (function() { function ToIndex(string_value, grade) { + if (typeof string_value !== "string") { + throw new TypeError("expected string for string_value."); + } + + string_value = string_value.trim(); + // string_value shouldn't have any spaces now, if it does throw an error. + if (string_value.indexOf(" ") !== -1) { + throw new TypeError("string_value shouldn't have any spaces."); + } + if (!grade.hasOwnProperty("value")) { throw new TypeError("invalid grade parameter."); } @@ -86,9 +96,43 @@ var ClimbGrades = (function() { throw new TypeError("grade.value is invalid."); } + // Slashes are only allowed in UIAA, sorry. + if (grade != GRADE.UIAA && string_value.indexOf("/") !== -1) { + throw new TypeError("Slash (/) grades are only supported for UIAA ratings."); + } + + // Accept that YDS or FRENCH might specify a plus or - at the end. - is + // ignored and removed, but plus offers a hint to the resolver to choose the + // highest index that matches the grade. + var find_highest = false; + if (grade == GRADE.YDS || + (grade == GRADE.FRENCH && parseInt(string_value[0]) <= 5)) { + var pos = string_value.indexOf("-"); + if (pos !== -1) { + if (pos != (string_value.length - 1)) { + throw new TypeError("a YDS or FRENCH grade allows a - only at the end."); + } + string_value = string_value.substr(0, string_value.length - 1); + } + pos = string_value.indexOf("+"); + if (pos !== -1) { + if (pos != (string_value.length - 1)) { + throw new TypeError("a YDS or FRENCH grade allows a + only at the end."); + } + find_highest = true; + string_value = string_value.substr(0, string_value.length - 1); + } + } + var column = grade.value; for (var i = 0; i < chart.length; i++) { if (chart[i][column] === string_value) { + if (find_highest === true) { + // Keep looking higher. + var j = i + 1; + while (j < chart.length && chart[j][column] === string_value) j++; + return j - 1; + } return i; } } @@ -104,6 +148,9 @@ var ClimbGrades = (function() { return module; }()); +exports = module.exports = ClimbGrades; + +/* function ParseSportGradeFromUIAA(uiaa) { var trimmed_uiaa = uiaa.trim(); var index = ClimbGrades.ToIndex(trimmed_uiaa, ClimbGrades.GRADE.UIAA); @@ -151,3 +198,4 @@ function Main() { } Main(); +*/ diff --git a/climbgrades/test/tests.js b/climbgrades/test/tests.js new file mode 100644 index 0000000..9ac348a --- /dev/null +++ b/climbgrades/test/tests.js @@ -0,0 +1,69 @@ +var grades = require("../climbgrades"); + +exports["basic"] = function(test) { + test.equal(8, grades.ToIndex("4+", grades.GRADE.UIAA)); + test.equal(7, grades.ToIndex("4a", grades.GRADE.FRENCH)); + test.equal(15, grades.ToIndex("5.8", grades.GRADE.YDS)); + test.equal("5.8", grades.FromIndex(15, grades.GRADE.YDS)); + test.equal("5a", grades.FromIndex(15, grades.GRADE.FRENCH)); + test.equal("5+/6-", grades.FromIndex(15, grades.GRADE.UIAA)); + test.done(); +} + + +exports["trimming"] = function(test) { + test.equal(8, grades.ToIndex(" 4+", grades.GRADE.UIAA)); + test.equal(7, grades.ToIndex("4a ", grades.GRADE.FRENCH)); + test.equal(15, grades.ToIndex(" 5.8 ", grades.GRADE.YDS)); + test.done(); +} + + +exports["bad_arguments"] = function(test) { + test.throws(function() { grades.ToIndex(undefined, grades.GRADE.UIAA); }); + test.throws(function() { grades.ToIndex(34, grades.GRADE.UIAA); }); + test.throws(function() { grades.ToIndex([,], grades.GRADE.UIAA); }); + + test.throws(function() { grades.ToIndex("4", grades.GRADE.blah); }); + test.throws(function() { grades.ToIndex("4", undefined); }); + + // No spaces allowed around slashes. + test.throws(function() { grades.ToIndex("4 / 4+", grades.GRADE.UIAA); }); + + // No slash grades allowed for non-UIAA ratings. + test.throws(function() { grades.ToIndex("5.7/5.8", grades.GRADE.YDS); }); + test.throws(function() { grades.ToIndex("5a/5b", grades.GRADE.FRENCH); }); + + test.throws(function() { grades.ToIndex("5.+7", grades.GRADE.YDS); }); + test.throws(function() { grades.ToIndex("+5.7", grades.GRADE.YDS); }); + test.throws(function() { grades.ToIndex("5.7-+", grades.GRADE.YDS); }); + + test.equal(-1, grades.ToIndex("invalid_thing", grades.GRADE.FRENCH)); + + test.throws(function() { grades.FromIndex(-1, grades.GRADE.UIAA); }); + test.throws(function() { grades.FromIndex(1000, grades.GRADE.UIAA); }); + test.throws(function() { grades.FromIndex("3.5", grades.GRADE.UIAA); }); + test.throws(function() { grades.FromIndex(null, grades.GRADE.UIAA); }); + + test.throws(function() { grades.FromIndex(10, grades.GRADE.blah); }); + test.throws(function() { grades.FromIndex(15, undefined); }); + + test.done(); +} + +exports["YDS_accomodation"] = function(test) { + test.equal(13, grades.ToIndex("5.7", grades.GRADE.YDS)); + test.equal(13, grades.ToIndex("5.7-", grades.GRADE.YDS)); + test.equal(14, grades.ToIndex("5.7+", grades.GRADE.YDS)); + test.equal(9, grades.ToIndex("5.5+", grades.GRADE.YDS)); + test.done(); +} + + +exports["FRENCH_accomodation"] = function(test) { + test.equal(22, grades.ToIndex("6a+", grades.GRADE.FRENCH)); + test.equal(19, grades.ToIndex("5c", grades.GRADE.FRENCH)); + test.equal(19, grades.ToIndex("5c-", grades.GRADE.FRENCH)); + test.equal(20, grades.ToIndex("5c+", grades.GRADE.FRENCH)); + test.done(); +}