diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..826c31e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.idea
+*.htm
\ No newline at end of file
diff --git a/README.md b/README.md
index 457a43d..b2efd4f 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,5 @@
# dclstats
-Consolidated stats from the DCL, used by the Self-Destruct Sequence podcast.
+
+A node application that uses the DCL pilot API written by Catherine "Drakona" Darrow to compile weekly stats from the [Descent Champion's Ladder](http://www.descentchampions.org) for the [Self-Destruct Sequence Podcast](http://www.selfdestructsequence.org).
+
+To run, use `node dcl` from the command line. It will output the file `dcl.htm` in its own directory.
diff --git a/dcl.js b/dcl.js
new file mode 100644
index 0000000..ef72757
--- /dev/null
+++ b/dcl.js
@@ -0,0 +1,510 @@
+var http = require("http"),
+ fs = require("fs"),
+ date = />[a-zA-Z]+, ([a-zA-Z]+ [0-9]{2}) new Date()) {
+ parsedDate = new Date(parsedDate.getFullYear() - 1, parsedDate.getMonth(), parsedDate.getDay());
+ }
+
+ if (parsedDate < lastDate) {
+ doCallback = false;
+ }
+ }
+
+ while((results = pilotRx.exec(body)) !== null) {
+ pilots[results[1]] = true;
+ }
+
+ callback();
+ });
+ }).on("error", function(err) {
+ console.log("Error while retrieving page " + pageId + " from DCL.");
+ console.log(err);
+ callback({
+ error: "Bad response from DCL.",
+ status: 502
+ });
+ });
+ },
+
+ getNextPage = function(err) {
+ if (err) {
+ console.log("Error getting page.");
+ console.log(err);
+ return;
+ }
+
+ if (index < 10 && doCallback) {
+ getPage(index, getNextPage);
+ index++;
+ return;
+ }
+
+ getNextPilot();
+ },
+
+ getNextPilot = function(err) {
+ var matches = {},
+ sortedPilots = {},
+ html = "",
+ totalMatches = 0,
+ pilotId, index, index2, match, pilot, game,
+
+ compare = function(a, b) {
+ return b.rank - a.rank;
+ },
+
+ gameHtml = function(game, threatening, domination) {
+ if (threatening && game.pilot.score >= 15) {
+ if (game.pilot.score > game.opponent.score) {
+ return "(link) W " + game.pilot.score + " to " + game.opponent.score + " " + game.opponent.name + " (" + game.suicides + " suicides) " + game.game + " " + game.map + " DARK HORSE" + (game.trophy !== 0 ? " Trophy Match" : "") + "
";
+ } else {
+ return "(link) L " + game.pilot.score + " to " + game.opponent.score + " " + game.opponent.name + " (" + game.suicides + " suicides) " + game.game + " " + game.map + " THREAT" + (game.trophy !== 0 ? " Trophy Match" : "") + "
";
+ }
+ } else {
+ if (domination !== false && game.opponent.score <= domination) {
+ return "(link) W " + game.pilot.score + " to " + game.opponent.score + " " + game.opponent.name + " (" + game.suicides + " suicides) " + game.game + " " + game.map + " DOMINATION" + (game.trophy !== 0 ? " Trophy Match" : "") + "
";
+ } else {
+ return "(link) " + (game.pilot.score > game.opponent.score ? "W" : "L") + " " + game.pilot.score + " to " + game.opponent.score + " " + game.opponent.name + " (" + game.suicides + " suicides) " + game.game + " " + game.map + (game.trophy !== 0 ? " Trophy Match" : "") + "
";
+ }
+ }
+ };
+
+ if (err) {
+ console.log("Error getting pilots.");
+ console.log(err);
+ return;
+ }
+
+ for (pilotId in pilots) {
+ if (pilots.hasOwnProperty(pilotId)) {
+ if (pilots[pilotId] === true) {
+ getPilot(pilotId, getNextPilot);
+ return;
+ }
+ }
+ }
+
+ for (pilotId in pilots) {
+ if (pilots.hasOwnProperty(pilotId)) {
+ pilots[pilotId].totalGames = 0;
+ pilots[pilotId].games = {};
+ pilots[pilotId].record = {};
+ for (index in pilots[pilotId].matches) {
+ if (pilots[pilotId].matches.hasOwnProperty(index)) {
+ match = pilots[pilotId].matches[index];
+ if (new Date(match.date) < firstRealDate && new Date(match.date) >= lastRealDate) {
+ pilots[pilotId].totalGames++;
+
+ if (!pilots[pilotId].games.hasOwnProperty(match.opponent.rating)) {
+ pilots[pilotId].games[match.opponent.rating] = [];
+ }
+ pilots[pilotId].games[match.opponent.rating].push(match);
+
+ if (!pilots[pilotId].record.hasOwnProperty(match.opponent.rating)) {
+ pilots[pilotId].record[match.opponent.rating] = {wins: 0, losses: 0};
+ }
+
+ if (match.pilot.score > match.opponent.score) {
+ totalMatches++;
+ if (!matches.hasOwnProperty(match.pilot.rating + " wins vs " + match.opponent.rating)) {
+ matches[match.pilot.rating + " wins vs " + match.opponent.rating] = 0;
+ }
+ matches[match.pilot.rating + " wins vs " + match.opponent.rating]++;
+ pilots[pilotId].record[match.opponent.rating].wins++;
+ } else {
+ pilots[pilotId].record[match.opponent.rating].losses++;
+ }
+ }
+ }
+ }
+
+ if (pilots[pilotId].totalGames > 0) {
+ if (!sortedPilots.hasOwnProperty(pilots[pilotId].rating)) {
+ sortedPilots[pilots[pilotId].rating] = [];
+ }
+ sortedPilots[pilots[pilotId].rating].push(pilots[pilotId]);
+ }
+ }
+ }
+
+ html = "";
+ html = html + "