UNPKG

koc

Version:

Library to parse data from the online MMORPG Kings of Chaos

1,399 lines (1,335 loc) 56.1 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: parser.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: parser.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>/*jslint evil: true */ /** @exports parser * KoC Parser - To parse KoC HTML pages into JS Objects */ var parser = {}; var helpers = require('./helpers'); /** * Parse and return the turing from given HTML content * @param {String} html HTML content to be parsed * @return {String} Turing, if found, empty if not */ parser.getTuring = function (html) { var re = /name="turing"\s*value="([^"]+)">/gmi; var m = re.exec(html); if (m !== null) return m[1]; return ""; }; parser.parseErrorMessageRed = function (html) { var re = /&lt;font\s*color="red">([^&lt;]+)/gmi; var m = re.exec(html); if (m !== null) return m[1]; return ""; }; /** * Get the error message from a KoC error.php page * @param {String} html the HTML page to parse the error from * @return {String} the error message, if found, "Unknown Error" otherwise */ parser.parseErrorMessage = function (html) { var re = /&lt;h3>Error&lt;\/h3>([^&lt;]+)&lt;/gmi; var m = re.exec(html); if (m !== null) { return m[1].trim(); } return "Unknown Error"; }; /** * Get the reason for being banned from a KoC bansuspend.php page * @param {String} html the HTML page to parse the error from * @return {String} the reason, if found, "Unknown Reason" otherwise */ parser.parseBannedMessage = function (html) { var re = /Banned&lt;\/[^>]*>\s*([^&lt;]+)/gmi; var m = re.exec(html); if (m !== null) { return m[1].trim(); } return "Unknown Reason"; }; /** * Parses a battlefield page in HTML format and returns an array of players * @param {String} html the content of the HTML page to parse * @return {Object} an object with an array of the players information found on that page */ parser.parseBattlefield = function (html) { var reLoggedIn = /&lt;td align="center" valign="middle" style="padding: 0">\s*(.*)\s*(.*)\s*&lt;\/td>\s*&lt;td.*>&lt;a href="alliances\.php\?id=([^"]*)">\s*([^&lt;]*)&lt;\/a>&lt;\/td>\s*&lt;td>&lt;a class="player" href="\/stats\.php\?id=([0-9]+)"\s*>([^&lt;]+)&lt;\/a>&lt;\/td>[^>]*>([0-9,]+)&lt;\/td>[^>]*>\s*([A-Za-z0-9-_]+)\s*&lt;\/td>[^>]*>([^G]+)Gold&lt;\/td>[^>]*>([^&lt;]+)&lt;\/td>/gmi; var reLoggedOut = /&lt;a href="alliances\.php\?id=([^"]*)">\s*([^&lt;]*)&lt;\/a>&lt;\/td>\s*&lt;td>&lt;a class="player" href="\/stats\.php\?id=([0-9]+)"\s*>([^&lt;]+)&lt;\/a>&lt;\/td>[^>]*>([0-9,]+)&lt;\/td>[^>]*>\s*([A-Za-z0-9-_]+)\s*&lt;\/td>[^>]*>([^G]+)Gold&lt;\/td>[^>]*>([^&lt;]+)&lt;\/td>/gmi; var players = []; var loggedIn = false; var m; [ { re: reLoggedIn, loggedIn: true }, { re: reLoggedOut, loggedIn: false } ].forEach(function (currentCase) { if (!players.length) { // do not check reLoggedOut if reLoggedIn worked while ((m = currentCase.re.exec(html)) !== null) { var startFrom = 1; var player = {}; if (currentCase.loggedIn) { loggedIn = true; startFrom = 3; player.buddyStatus = helpers.parseBuddyStatus(m[1]); // Buddy Information player.intelFile = helpers.parseIntelFile(m[2]); // Spy Information } player.alliance = { id: m[startFrom], name: m[startFrom + 1].replace("&amp;nbsp;",""), }; player.userid = m[startFrom + 2]; player.username = m[startFrom + 3]; player.armySizeText = m[startFrom + 4]; player.armySize = helpers.quantityFromString(m[startFrom + 4]); player.race = m[startFrom + 5]; player.goldText = m[startFrom + 6].trim(); player.gold = helpers.quantityFromString(m[startFrom + 6], -1); player.rankText = m[startFrom + 7]; player.rank = helpers.quantityFromString(m[startFrom + 7], -1); players.push(player); } } }); // Pages var reTotal = />(.*) players total \| page (.*) of ([^&lt;]+)/gmi; var playersTotal = 0, currentPage = 0, maxPage = 0; var mTotal = reTotal.exec(html); if (mTotal !== null) { playersTotal = helpers.quantityFromString(mTotal[1]); currentPage = helpers.quantityFromString(mTotal[2]); maxPage = helpers.quantityFromString(mTotal[3]); } return { players: players, playersTotal: playersTotal, currentPage: currentPage, maxPage: maxPage, loggedIn: loggedIn, success: true, }; }; /** * Get user info from the base HTML page * @param {String} baseHtml HTML content of the Base page * @return {Object} an object containing user information */ parser.parseBase = function (baseHtml) { //var re=/&lt;tr>\s*&lt;td>\s*&lt;b>Name&lt;\/b>\s*&lt;\/td>\s*&lt;td>\s*&lt;a href="stats\.php\?id=([0-9]+)">([^&lt;]+)&lt;\/a>\s*&lt;\/td>\s*&lt;\/tr>/mg; //var re = new RegExp(createTrRegExp("Name", '&lt;a href="stats\\.php\\?id=([0-9]+)">([^&lt;]+)&lt;\\/a>'), 'gm'); try { var reUserInfo = new RegExp(helpers.createTableRegExp([{ Key: "Name", Value: '&lt;a href="stats\\.php\\?id=([0-9]+)">([^&lt;]+)&lt;\\/a>' // 1, 2 }, { Key: "Race", Value: '([^&lt;]+)' // 3 }, { Key: "Rank", Value: '([^&lt;]+)' // 4 }, { Key: "Highest Rank", Value: '([^&lt;]+)' // 5 }]), 'gm'); var matches = reUserInfo.exec(baseHtml); var reCommander = /Commander&lt;\/b>\s*&lt;\/td>\s*&lt;td>\s*&lt;a.*\?id=([0-9]+)">([^&lt;]+)&lt;\/a>/gmi; var mCommander = reCommander.exec(baseHtml); var commander = (mCommander === null) ? { username: '', userid: -1 } : { username: mCommander[2], userid: mCommander[1] }; var reMilitary = new RegExp(helpers.createTableRegExp([{ Key: "Fortification", Value: '([^&lt;]+)' // 1 }, { Key: "Siege Technology", Value: '([^&lt;]+)' // 2 }, { Key: "Economy", Value: '([^&lt;]+)' // 3 }, { Key: "Technology", Value: '([^&lt;]+)' // 4 }, { Key: "Conscription", Value: '([^&lt;]+)' // 5 }, { Key: "Available Funds", Value: '([^&lt;]+)' // 6 }, { Key: "Projected Income", Value: '([^&lt;]+)' // 7 }, { Key: "Game Turns", Value: '([^&lt;]+)' // 8 }, { Key: "Covert Level", Value: '([^&lt;]+)' // 9 }]), 'gm'); var matchesMilitary = reMilitary.exec(baseHtml); var reMilitaryEffectiveness = new RegExp(helpers.createTableTripleTdRegExp([{ Key: "&lt;b>Strike Action&lt;\\/b>", Value1: '([^&lt;]+)', // 3 Value2: '([^&lt;]+)' // 5 }, { Key: "&lt;b>Defensive Action&lt;\\/b>", Value1: '([^&lt;]+)', // 8 Value2: '([^&lt;]+)' // 10 }, { Key: "&lt;b>Spy Rating&lt;\\/b>", Value1: '([^&lt;]+)', // 13 Value2: '([^&lt;]+)' // 15 }, { Key: "&lt;b>Sentry Rating&lt;\\/b>", Value1: '([^&lt;]+)', // 18 Value2: '([^&lt;]+)' // 20 }]), 'gm'); var matchesMilitaryEffectiveness = reMilitaryEffectiveness.exec(baseHtml); var rePreviousLogins = new RegExp(helpers.createTableTripleTdRegExp([{ Key: "([0-9.]+)", // 2 Value1: '([^&lt;]+)', // 4 Value2: '([^&lt;]+)' // 6 }]), 'gm'); var previousLogins = []; var matchesPreviousLogins; while ((matchesPreviousLogins = rePreviousLogins.exec(baseHtml)) !== null) { previousLogins.push({ ip: matchesPreviousLogins[2], date: matchesPreviousLogins[4], success: matchesPreviousLogins[6] }); } var rePersonnel = new RegExp(helpers.createTableTripleTdRegExp([{ Key: "&lt;b>Trained Attack Soldiers&lt;\\/b>", Value1: '([^&lt;]+)', // 3 Value2: '([^&lt;]+)&lt;a([^>]+)>([^&lt;]+)&lt;\\/a>([^&lt;]+)' }, { Key: "&lt;b>Trained Attack Mercenaries&lt;\\/b>", Value1: '([^&lt;]+)', // 11 Value2: '([^&lt;]+)&lt;a([^>]+)>([^&lt;]+)&lt;\\/a>([^&lt;]+)' }, { Key: "&lt;b>Trained Defense Soldiers&lt;\\/b>", Value1: '([^&lt;]+)', // 19 Value2: '([^&lt;]+)&lt;a([^>]+)>([^&lt;]+)&lt;\\/a>([^&lt;]+)' }, { Key: "&lt;b>Trained Defense Mercenaries&lt;\\/b>", Value1: '([^&lt;]+)', // 27 Value2: '([^&lt;]+)&lt;a([^>]+)>([^&lt;]+)&lt;\\/a>([^&lt;]+)' }, { Key: "&lt;b>Untrained Soldiers&lt;\\/b>", Value1: '([^&lt;]+)', // 35 Value2: '([^&lt;]+)&lt;a([^>]+)>([^&lt;]+)&lt;\\/a>([^&lt;]+)' }, { Key: "&lt;b>Untrained Mercenaries&lt;\\/b>", Value1: '([^&lt;]+)', // 43 Value2: '([^&lt;]+)&lt;a([^>]+)>([^&lt;]+)&lt;\\/a>([^&lt;]+)' }, { Key: "&lt;b>Spies&lt;\\/b>", Value1: '([^&lt;]+)', // 51 Value2: '([^&lt;]+)&lt;a([^>]+)>([^&lt;]+)&lt;\\/a>([^&lt;]+)' }, { Key: "&lt;b>Sentries&lt;\\/b>", Value1: '([^&lt;]+)', // 59 Value2: '([^&lt;]+)&lt;a([^>]+)>([^&lt;]+)&lt;\\/a>([^&lt;]+)' }, { Key: "&lt;b>Army Morale&lt;\\/b>", Value1: '([^&lt;]+)', // 67 Value2: '([^&lt;]+)&lt;a([^>]+)>([^&lt;]+)&lt;\\/a>([^&lt;]+)' }, { Key: "&lt;b>Total Fighting Force&lt;\\/b>", Value1: '([^&lt;]+)', // 75 Value2: '([^&lt;]+)&lt;a([^>]+)>([^&lt;]+)&lt;\\/a>([^&lt;]+)' }]), 'gm'); var matchesPersonnel = rePersonnel.exec(baseHtml); //if(matches!==null) this.username = matches[2]; // Race Change var reRaceChangesLeft = /([0-9]+) changes left/gmi; var mRaceChangesLeft = reRaceChangesLeft.exec(baseHtml); var raceChangesLeft = (mRaceChangesLeft !== null) ? Number(mRaceChangesLeft[1]) : -1; // Recent Attacks var reRecentAttacks = /&lt;tr>\s*&lt;td>(.*)&lt;\/td>\s*&lt;td>\s*&lt;a.*href="stats\.php\?id=([0-9]+)".*>(.*)&lt;\/a>.*&lt;\/td>.*&lt;td>([^&lt;]+)/gmi; var mRecentAttacks; var recentAttacks = []; while ((mRecentAttacks = reRecentAttacks.exec(baseHtml)) !== null) { recentAttacks.push({ date: mRecentAttacks[1], enemy: { username: mRecentAttacks[3], userid: mRecentAttacks[2] }, result: mRecentAttacks[4] }); } return { success: true, userInfo: { userid: matches[1], username: matches[2], race: matches[3].trim(), rank: matches[4], highestRank: matches[5], commander: commander }, militaryOverview: { fortification: matchesMilitary[1], siegeTechnology: matchesMilitary[2], economy: matchesMilitary[3], technology: matchesMilitary[4], conscription: matchesMilitary[5], availableFunds: matchesMilitary[6], projectedIncome: matchesMilitary[7], gameTurns: matchesMilitary[8].trim().split('/')[0].trim(), covertLevel: matchesMilitary[9] }, militaryEffectiveness: { strikeAction: matchesMilitaryEffectiveness[3], strikeActionRank: matchesMilitaryEffectiveness[5].replace(/Ranked #/gm, ''), defensiveAction: matchesMilitaryEffectiveness[8], defensiveActionRank: matchesMilitaryEffectiveness[10].replace(/Ranked #/gm, ''), spyRating: matchesMilitaryEffectiveness[13], spyRatingRank: matchesMilitaryEffectiveness[15].replace(/Ranked #/gm, ''), sentryRating: matchesMilitaryEffectiveness[18], sentryRatingRank: matchesMilitaryEffectiveness[20].replace(/Ranked #/gm, '') }, personnel: { trainedAttackSoldiers: matchesPersonnel[3], trainedAttackMercenaries: matchesPersonnel[11], trainedDefenseSoldiers: matchesPersonnel[19], trainedDefenseMercenaries: matchesPersonnel[27], untrainedSoldiers: matchesPersonnel[35], untrainedMercenaries: matchesPersonnel[43], spies: matchesPersonnel[51], sentries: matchesPersonnel[59], armyMorale: matchesPersonnel[67], totalFightingForce: matchesPersonnel[75] }, previousLogins: previousLogins, raceChangesLeft: raceChangesLeft, recentAttacksOnYou: recentAttacks }; } catch (e) { return { success: false, error: "An error occurred parsing the base: " + e.toString() }; } }; /** * Parse the box on the left-side of each page with the stats * @param {String} html the HTML content of the page to parse * @return {Object} an object containing gold, experience, turns, rank, * lastAttacked, mails and newMails if found */ parser.parseLeftSideBox = function (html) { var result = {}; if (html === undefined || html === null || !html.length) return result; // Username and Fortification var re = /&lt;title>Kings of Chaos :: ([a-zA-Z0-9]+)'s ([^&lt;]+)&lt;\/title>/i; var m = re.exec(html); result.username = (m !== null) ? m[1] : "???"; result.fortification = (m !== null) ? m[2] : "???"; // Gold re.compile(/Gold:[^>]+>\s*([^\n&lt;]+)/gmi); m = re.exec(html); result.goldText = (m !== null) ? m[1] : "???"; result.gold = (m !== null) ? helpers.quantityFromString(m[1], 0) : 0; result.success = m !== null; // Experience re.compile(/Experience:\s*&lt;[^>]*>\s*&lt;[^>]*>\s*&lt;[^>]*>\s*([^\n&lt;]+)/gmi); m = re.exec(html); result.experienceText = (m !== null) ? m[1] : "???"; result.experience = helpers.quantityFromString(result.experienceText); // Turns re.compile(/Turns:\s*&lt;[^>]*>\s*&lt;[^>]*>\s*&lt;[^>]*>\s*([^\n&lt;]+)/gmi); m = re.exec(html); result.turns = (m !== null) ? m[1] : "???"; // Rank re.compile(/Rank:\s*&lt;[^>]*>\s*&lt;[^>]*>\s*&lt;[^>]*>\s*([^\n&lt;]+)/gmi); m = re.exec(html); result.rank = (m !== null) ? m[1] : "???"; // Last Attacked re.compile(/Last Attacked:\s*&lt;[^>]+>\s*&lt;[^>]+>([^\n&lt;]+)/gmi); m = re.exec(html); result.lastAttacked = (m !== null) ? m[1] : "???"; // Mails re.compile(/Mail:\s*&lt;a\s*href="[^"]*"\s*style="font-size:\s*[^;]*;\s*color:\s*([^;]+);">([^\n&lt;]+)/gmi); m = re.exec(html); result.mails = (m !== null) ? m[2] : "???"; result.newMails = !!(m !== null &amp;&amp; m[1] == "RED"); return result; }; /** * Get available races information * @param {String} html HTML content of the page to parse */ parser.parseRacesInfo = function (html) { var result = {}; if (html === undefined || html === null || !html.length) return result; // Background colours var re = /&lt;th\s*class="([^"]+)"\s*style="background-color:\s*([^;]+);\s*border:\s*([^ ]+)\s*[^;]*;">/gmi; var m; var colours = {}; while ((m = re.exec(html)) !== null) { colours[m[1]] = { background: m[2], border: m[3] }; } // Colours re.compile(/style="color:([^"]+)" value="Join ([^!]+)!/gmi); while ((m = re.exec(html)) !== null) { if (colours[m[2]] === undefined) colours[m[2]] = {}; colours[m[2]].text = m[1]; } // Images re.compile(/&lt;td\s*class="([^"]+)"\s*align="[^"]+">\s*&lt;img\s*src="([^"]+)"/gmi); var images = {}; while ((m = re.exec(html)) !== null) { images[m[1]] = /*this.getKoCHost() +*/ m[2]; } // Features re.compile(/&lt;td\s*class="([^"]+)"\s*align="center"\s*>([^:]+):&lt;br \/>\s*&lt;b>\s*([^&lt;]+)\s*&lt;\/b>\s*&lt;br \/>\s*&lt;b>\s*([^&lt;]+)\s*/gmi); var races = []; while ((m = re.exec(html)) !== null) { races.push({ race: m[1], description: m[2], features: [ m[3], m[4] ], image: images[m[1]], colours: colours[m[1]] }); } return races; }; /** * Get the New User Advisor content, when available * @param {String} html HTML content of the page to parse */ parser.parseNewUserAdvisor = function (html) { if (html === undefined || html === null || !html.length) return ''; var re = /New\s*User\s*Advisor&lt;[^>]*>\s*&lt;[^>]*>\s*&lt;[^>]*>\s*&lt;[^>]*>\s*/gmi; var m = re.exec(html); if (m === null) return ''; var startIndex = re.lastIndex; re = /&lt;\/td>/gmi; re.lastIndex = startIndex; m = re.exec(html); if (m === null) return ''; var endIndex = m.index; var rawResult = html.substring(startIndex, endIndex); var strippedResult = helpers.stripHtml(rawResult); return strippedResult.replace(/\s+/gmi, " "); }; /** * Guess the age from the HTML on the page * @param {String} html HTML content of the page to parse * @return {Number} KoC Age */ parser.guessAge = function (html) { if (html === undefined || html === null || !html.length) return 0; var re = /age([0-9]+)\./gmi; var m = re.exec(html); if (m === null) return -1; return Number(m[1]); }; /** * Parse the menu on the left side * @param {String} html HTML content of the page to parse * @return {Array} all the items in the menu (containing page, image and title) */ parser.parseMenu = function (html) { var result = []; if (html === undefined || html === null || !html.length) return result; var re = /&lt;a.*href="([^"]+)">.*&lt;img.*alt="([^"]+)".*src="([^"]+)"[^>]*.*width="137".*>/gmi; var m; while ((m = re.exec(html)) !== null) { result.push({ page: m[1], title: m[2], image: m[3] }); } return result; }; /** * Get the number of times we can change the commander * and the statement to copy (image) * @return {Object} If succeeded, returns an object with 'success', * 'nbTimesCanChangeCommander', 'errorMessage' and 'statement' properties */ parser.parseCommanderChange = function (html) { var result = {}; if (html === undefined || html === null || !html.length) return result; var re = /you may only do this\s*(.*)\s*times during the age/gmi; var m1 = re.exec(html); var nbTimesCanChange = (m1 === null) ? -1 : Number(helpers.stripHtml(m1[1])); re = /&lt;p>\s*&lt;font\s*color="RED"\s*>(.*)&lt;\/font>\s*&lt;p>/gmi; var m2 = re.exec(html); var errorMessage = (m2 === null) ? "" : m2[1]; if (!errorMessage.length) { errorMessage = this.parseErrorMessage(html); if (errorMessage == "Unknown Error") errorMessage = ""; } re = /action="commander_change\.php".*>\s*&lt;img.*src="([^"]+)">/gmi; var m3 = re.exec(html); var statement = (m3 === null) ? "" : m3[1]; return { success: (m1 !== null) &amp;&amp; (m2 !== null) &amp;&amp; (m3 !== null), nbTimesCanChangeCommander: nbTimesCanChange, errorMessage: errorMessage, statement: statement }; }; /** * Parse the error message on a forgot pass page * @param {String} html the HTML page to parse */ parser.parseForgotPass = function (html) { var result = {}; if (html === undefined || html === null || !html.length) return result; var re = /&lt;p>\s*&lt;font\s*color="RED"\s*>(.*)&lt;\/font>\s*&lt;p>/gmi; var m = re.exec(html); var errorMessage = (m === null) ? "" : m[1]; if (!errorMessage.length) { errorMessage = this.parseErrorMessage(html); if (errorMessage == "Unknown Error") errorMessage = ""; } return helpers.stripHtml(errorMessage); }; parser.parseOfficers = function (html) { }; parser.parseFullStats = function (html) { var result = {}; if (html === undefined || html === null || !html.length) return { success: false, error: "Empty page to parse", }; // Turing var turing = this.getTuring(html); result.turing = turing; // Main User Stats var reNameCommander = /Name:[\s&lt;tdrbpa>\/\\]*&lt;td>\s*([^\s]+)\s*&lt;\/td>[\s&lt;tdrbpa>\/\\]*Commander:\s*&lt;\/b>\s*&lt;\/td>\s*&lt;td[^>]*>\s*(.*)\s*&lt;\/td>/gmi; var mNameCommander = reNameCommander.exec(html); var reSupremeCommander = /Supreme Commander\s*&lt;\/b>\s*&lt;\/td>\s*&lt;td[^>]*>\s*&lt;a href="stats\.php\?id=([0-9]+)">([^&lt;]+)&lt;\/a>\s*&lt;\/td>[\s&lt;tdrbpa>\/\\]*Chain Name:[\s&lt;tdrbpa>\/\\]*&lt;td>\s*([^&lt;]+)/gmi; var mSupremeCommander = reSupremeCommander.exec(html); var supremeCommander = mSupremeCommander === null ? { username: "None", userid: -1, } : { username: mSupremeCommander[2], userid: helpers.quantityFromString(mSupremeCommander[1]), }; var chainName = mSupremeCommander === null ? "" : mSupremeCommander[3]; var reUserStats = /Alliances:\s*&lt;\/b>\s*&lt;\/td>\s*&lt;td>\s*(.*)\s*&lt;\/td>[\s&lt;tdrbpa>\/\\]*Race:[\s&lt;tdrbpa>\/\\]*&lt;td>\s*([^&lt;\s]+)[\s&lt;tdrbpa>\/\\]*Rank:[\s&lt;tdrbpa>\/\\]*&lt;td>([^&lt;\s]+)[\s&lt;tdrbpa>\/\\]*Highest Rank:[\s&lt;tdrbpa>\/\\]*([^&lt;]+)[\s&lt;tdrbpa>\/\\]*Army\s*Size:[\s&lt;tdrbpa>\/\\]*([^&lt;\s]+)[\s&lt;tdrbpa>\/\\]*Army Morale:[\s&lt;tdrbpa>\/\\]*([^&lt;\s]+)/gmi; var mUserStats = reUserStats.exec(html); if (mUserStats === null) return { success: false, error: "Failed to parse user stats", turing: turing, }; result.success = true; // Gold var reGold = /Treasury\s*:\s*.*\s*&lt;td>([^&lt;]*)/gmi; var mGold = reGold.exec(html); var goldText = (mGold !== null) ? mGold[1] : "???"; var gold = (mGold !== null) ? helpers.quantityFromString(mGold[1], 0) : 0; // Fortification / Buddy Status var reFortBuddy = /[\s&lt;tdrbpa>\/\\]*Fortifications\s*:[\s&lt;tdrbpa>\/\\]*>\s*([\s&lt;0-9a-z=>\/\\]+)\s*&lt;\/td>[\s&lt;a-z=>\/\\"0-9]*Buddy\s*List\s*Status\s*:[\s&lt;a-z=>\/\\"]*>\s*([\s&lt;0-9a-z=>\/\\]+)\s*&lt;\/td>/gmi; var mFortBuddy = reFortBuddy.exec(html); var fortification = "", buddyStatus = ""; if (mFortBuddy !== null) { fortification = mFortBuddy[1]; buddyStatus = mFortBuddy[2]; } // Parse the commander var reCommander = /id=(\d*)">([^&lt;]*)/gmi; var mCommander = reCommander.exec(mNameCommander[2]); var commanderUsername = (mCommander === null) ? mNameCommander[2] : mCommander[2]; var commanderUserid = (mCommander === null) ? -1 : helpers.quantityFromString(mCommander[1]); // Parse the primary alliance var rePrimaryAlliance = /id=(\d*)">\s*([^&lt;]+)\s*&lt;\/a>\s*&lt;\/b>\s*\(\s*Primary/gmi; var mPrimaryAlliance = rePrimaryAlliance.exec(mUserStats[1]); var primaryAlliance = mPrimaryAlliance !== null ? { name: mPrimaryAlliance[2], id: helpers.quantityFromString(mPrimaryAlliance[1]), } : { name: "", id: -1, }; // Parse the alliance(s) var reAlliances = /id=(\d*)">([^&lt;]*)/gmi; var mAlliances; var alliances = []; while ((mAlliances = reAlliances.exec(mUserStats[1])) !== null) { alliances.push({ name: mAlliances[2], id: mAlliances[1], primary: mAlliances[2] == primaryAlliance.name }); } // Officers var reOfficers = /&lt;a href="stats\.php\?id=(\d+)"\s*>\s*([^&lt;]+)\s*&lt;\/a>\s*&lt;\/td>\s*&lt;td[^>]*>\s*([^&lt;]+)\s*&lt;\/td>\s*&lt;td[^>]*>\s*([^&lt;]+)\s*&lt;\/td>\s*&lt;td[^>]*>\s*([^&lt;]+)\s*&lt;\/td>/gmi; var mOfficers; var officers = []; while ((mOfficers = reOfficers.exec(html)) !== null) { officers.push({ username: mOfficers[2], userid: helpers.quantityFromString(mOfficers[1]), rank: mOfficers[3], armySize: mOfficers[4], race: mOfficers[5].trim() }); } // Total Officers var reTotalOfficers = />(.*) officers total/gmi; var mTotalOfficers = reTotalOfficers.exec(html); var totalOfficersNb = mTotalOfficers === null ? 0 : mTotalOfficers[1]; result.user = { username: mNameCommander[1], commander: { username: commanderUsername, userid: commanderUserid, }, supremeCommander: supremeCommander, chainName: chainName, race: mUserStats[2], rank: mUserStats[3], highestRank: mUserStats[4], armySize: mUserStats[5], armyMorale: mUserStats[6], fortification: fortification, buddyStatus: buddyStatus, gold: gold, goldText: goldText, alliances: alliances, primaryAlliance: primaryAlliance, officers: officers, totalOfficersNb: Number(totalOfficersNb), }; return result; }; /** * Parse the quick stats (xhr from the battlefield) * @param html * @returns {Object} parsed quick stats */ parser.parseQuickStats = function(html) { if (html === undefined || html === null || !html.length) return { success: false, error: "Empty page to parse", }; // Turing var turing = this.getTuring(html); // Full Profile Link var reFullProfile = /Full Profile:&lt;\/b>&lt;\/td>\s*&lt;td>&lt;a href="stats\.php\?id=([0-9]+)">([^&lt;]+)&lt;\/a>&lt;\/td>/i; var mFullProfile = reFullProfile.exec(html); if(mFullProfile===null){ return { success: false, error: "Failed parsing the full profile link in the quick stats", turing: turing, }; } var userid = helpers.quantityFromString(mFullProfile[1], -1); var username = mFullProfile[2]; // Commander var reCommander = /Commander:&lt;\/b>&lt;\/td>\s*&lt;td>([^\n]+)/i; var mCommander = reCommander.exec(html); var commander = (mCommander!==null) ? helpers.parseUser(mCommander[1]) : { username: '???' }; // Supreme Commander var reSupremeCommander = /Supreme Commander&lt;\/b>&lt;\/td>\s*&lt;td>([^\n]+)/i; var mSupremeCommander = reSupremeCommander.exec(html); var supremeCommander = (mSupremeCommander!==null) ? helpers.parseUser(mSupremeCommander[1]) : { username: 'None' }; // Highest Rank, Treasury, Fortification var reRankTreasuryFortification = /Highest Rank[^\n]*\s*&lt;td>([^\n]+)&lt;\/td>[\sa-zA-Z,-&lt;>\/\\]+Treasury[^\n]*\s*&lt;td>([^\n]+)&lt;\/td>[\sa-zA-Z,-&lt;>\/\\]+Fortifications[^\n]*\s*&lt;td>([^&lt;]+)/i; var mRankTreasuryFortification = reRankTreasuryFortification.exec(html); var highestRank = '???', fortification = '???', treasuryAsText = '???', treasury = -1; if(mRankTreasuryFortification!==null){ highestRank = mRankTreasuryFortification[1]; fortification = mRankTreasuryFortification[3]; treasury = helpers.quantityFromString(mRankTreasuryFortification[2]); treasuryAsText = helpers.stripHtml(mRankTreasuryFortification[2]); } // Buddy List Status var reBuddyStatus = /&lt;option\s*label="([^"]+)"\s*value="([^"]+)"\s*selected="selected">/i; var mBuddyStatus = reBuddyStatus.exec(html); var buddyStatus = (mBuddyStatus!==null) ? mBuddyStatus[1] : 'none'; return { success: true, userid: userid, username: username, commander: commander, supremeCommander: supremeCommander, highestRank: highestRank, treasury: treasury, treasuryAsText: treasuryAsText, fortification: fortification, buddyStatus: buddyStatus, turing: turing, } }; /** * Parse the armory from HTML to an object * @param html * @returns {Object} parsed armory */ parser.parseArmory = function (html) { if (html === undefined || html === null || !html.length) return { success: false, error: "Empty page to parse" }; // Weapon Categories var re = /class="subh"\s*align="left"\s*>\s*([^&lt;]+)/gmi; var m; var weaponCategories = []; while ((m = re.exec(html)) !== null) { weaponCategories.push({ name: m[1], index: m.index }); } // Current Weapons var reCurrentWeapons = />([^&lt;]+)&lt;\/td>\s*&lt;td\s*align="right">([^&lt;]+)&lt;\/td>\s*&lt;td\s*align="[rightcenter]+"\s*>([^&lt;]+)&lt;\/td>\s*&lt;td\s*align="center"\s*>\s*&lt;ta/gmi; var reCurrentTools = />([^&lt;]+)&lt;\/td>\s*&lt;td\s*align="right">([^&lt;]+)&lt;\/td>\s*&lt;td\s*align="[rightcenter]+"\s*>([^&lt;]+)&lt;\/td>\s*&lt;form/gmi; var currentWeapons = {}; var mCurrentWeapons; var getCategoryForIndex = function (index, categories) { var i = 1; while (categories[i].index &lt; index &amp;&amp; i &lt; categories.length) i++; return categories[--i].name; }; // Current Weapons - Sell Price var reSellFor = /input\s*type="input"\s*name="([^"]+).*\s*.*\s*.*\s*.*Sell for ([^"]+)/gmi; var mSellFor; var weaponsSellFor = []; while ((mSellFor = reSellFor.exec(html)) !== null) { weaponsSellFor.push({ inputName: mSellFor[1], priceText: mSellFor[2], price: helpers.quantityFromString(mSellFor[2]), }); } // Repair var reRepair = /onSubmit="[^"]+.*\s*.*\s*.*\s*.*name="([^"]+).*value="([^"]+).*\s*.*\s*.*\s*.*Repair for (.*) Gold/gmi; var mRepair; var repairWeapons = {}; while ((mRepair = reRepair.exec(html)) !== null) { var repairWeaponId = helpers.quantityFromString(mRepair[1]); repairWeapons[repairWeaponId] = { inputName: mRepair[1], defaultValue: helpers.quantityFromString(mRepair[2]), pricePerPoint: helpers.quantityFromString(mRepair[3]), }; } // Repair all var reRepairAll = /name="([^"]+).*Repair all weapons for (.*) Gold/gmi; var mRepairAll = reRepairAll.exec(html); var repairAll; if (mRepairAll !== null) { repairAll = { inputName: mRepairAll[1], priceText: mRepairAll[2], price: helpers.quantityFromString(mRepairAll[2]), }; } // Aggregate [reCurrentWeapons, reCurrentTools].forEach(function (regex) { while ((mCurrentWeapons = regex.exec(html)) !== null) { var index = mCurrentWeapons.index; var category = getCategoryForIndex(index, weaponCategories); if (currentWeapons[category] === undefined) currentWeapons[category] = []; var sell = {}; var weaponId = -1; if (weaponsSellFor.length) { sell = weaponsSellFor.shift(); weaponId = helpers.quantityFromString(sell.inputName); } var quantity = helpers.quantityFromString(mCurrentWeapons[2]); var strengthSplit = mCurrentWeapons[3].split('/'); var strengthCurrent = helpers.quantityFromString(mCurrentWeapons[3]); var strengthMax = strengthCurrent; if(strengthSplit.length>1){ strengthCurrent = helpers.quantityFromString(strengthSplit[0]); strengthMax = helpers.quantityFromString(strengthSplit[1]); } var currentWeapon = { name: mCurrentWeapons[1], weaponId: weaponId, quantity: quantity, strengthText: mCurrentWeapons[3], strengthCurrent: strengthCurrent, strengthMax: strengthMax, sell: sell, }; if(repairWeapons[weaponId]!==undefined){ currentWeapon.repair = repairWeapons[weaponId]; } currentWeapons[category].push(currentWeapon); } }); // Get available weapons var reBuyWeapons = /&lt;tr>\s*&lt;td>([^&lt;]+)&lt;\/td>\s*&lt;td\s*align="right"\s*>([^&lt;]+)&lt;\/td>\s*&lt;td\s*align="right"\s*>([^&lt;]+)&lt;\/td>\s*&lt;td\s*align="center"\s*>\s*&lt;input\s*type="text"\s*name="([^"]+)"\s*value="([^"]*)/gmi; var mBuyWeapons; var buyWeapons = {}; while ((mBuyWeapons = reBuyWeapons.exec(html)) !== null) { var index = mBuyWeapons.index; var category = getCategoryForIndex(index, weaponCategories); if (buyWeapons[category] === undefined) buyWeapons[category] = []; buyWeapons[category].push({ name: mBuyWeapons[1], strengthText: mBuyWeapons[2], strength: helpers.quantityFromString(mBuyWeapons[2]), price: helpers.quantityFromString(mBuyWeapons[3]), priceText: mBuyWeapons[3], inputName: mBuyWeapons[4], inputValue: helpers.quantityFromString(mBuyWeapons[5]), weaponId: helpers.quantityFromString(mBuyWeapons[4]), }); } // Fortification &amp; Siege var reUpgrades = /&lt;td>([^&lt;]+)&lt;\/td>\s*&lt;td\s*align="center">\s*&lt;input.*value="([^"]+)[^>]*>\s*.*\s*.*name="([^"]+)".*value="([^"]+)/gmi; var mUpgrades; var upgrades = []; while ((mUpgrades = reUpgrades.exec(html)) !== null) { upgrades.push({ current: mUpgrades[1], next: mUpgrades[2], inputName: mUpgrades[3], inputValue: mUpgrades[4] }); } if (upgrades.length == 2) { upgrades = { fortification: upgrades[0], siegeTechnology: upgrades[1] }; } // Military Effectiveness var reMilitaryEffectiveness = /&lt;td>&lt;b>([^&lt;]+)&lt;\/b>&lt;\/td>\s*&lt;td\s*align="right">([^&lt;]+)&lt;\/td>\s*&lt;td\s*align="right">([^&lt;]+)/gmi; var mMilitaryEffectiveness; var militaryEffectiveness = []; while ((mMilitaryEffectiveness = reMilitaryEffectiveness.exec(html)) !== null) { militaryEffectiveness.push({ name: mMilitaryEffectiveness[1], value: mMilitaryEffectiveness[2], rank: mMilitaryEffectiveness[3] }); } var personnel = this.parsePersonnel(html); return { currentWeapons: currentWeapons, buyWeapons: buyWeapons, upgrades: upgrades, militaryEffectiveness: militaryEffectiveness, personnel: personnel, repairAll: repairAll, turing: this.getTuring(html), error: this.parseErrorMessageRed(html), }; }; parser.parsePersonnel = function (html) { if (html === undefined || html === null || !html.length) return { success: false, error: "Empty page to parse" }; // Personnel var rePersonnel = /&lt;td.*>&lt;b>([^&lt;]+)&lt;\/b>&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;\//gmi; var mPersonnel; var personnel = {}; while ((mPersonnel = rePersonnel.exec(html)) !== null) { personnel[mPersonnel[1]] = helpers.quantityFromString(mPersonnel[2]); personnel[mPersonnel[1]+' Text'] = mPersonnel[2]; } return personnel; }; parser.parseTraining = function (html) { if (html === undefined || html === null || !html.length) return { success: false, error: "Empty page to parse" }; var personnel = this.parsePersonnel(html); var reTrain = /&lt;td>([^&lt;]+)&lt;\/td>\s*&lt;td\s*align="right">([^&lt;]+)&lt;\/td>\s*.*name="([^"]+).*value="([^"]+)/gmi; var mTrain; var train = []; while ((mTrain = reTrain.exec(html)) !== null) { train.push({ trainingProgram: mTrain[1], costPerUnitText: mTrain[2], costPerUnit: helpers.quantityFromString(mTrain[2]), inputName: mTrain[3], inputValue: helpers.quantityFromString(mTrain[4]), }); } var reUpgrades = [ /&lt;th colspan=2 align="center">([^&lt;]+)&lt;[A-Za-z&lt;>\s0-9"=\/]+&lt;th\s*class="subh"\s*align="[^"]+"\s*>([^&lt;]+)[A-Za-z&lt;>\s0-9"=\/]+&lt;th\s*class="subh"\s*>([A-Za-z\s&lt;>\/\(\)0-9]+)&lt;\/th>[A-Za-z&lt;>\s0-9"=\/]+&lt;td>([^&lt;]+)[A-Za-z&lt;>\s0-9"=\/]+&lt;input\s*type="submit"\s*value="([^"]+)"[A-Za-z&lt;>\s0-9"=\/]+&lt;input\s*type="hidden"\s*name="([^"]+)"\s*value="([^"]+)/gmi, /&lt;th colspan\s*=\s*[0-9"]+ align="center">([^&lt;]+)&lt;[A-Za-z&lt;>\s0-9"=\/]+&lt;th\s*class="subh"\s*align="[^"]+"\s*>([^&lt;]+)[A-Za-z&lt;>\s0-9"=\/]+&lt;th\s*class="subh"\s*>[A-Za-z&lt;>\s0-9"=\/]+&lt;input\s*type="submit"\s*value="([^(]+)\(([^)]+)\)"[A-Za-z&lt;>\s0-9"=\/]+&lt;input\s*type="hidden"\s*name="([^"]+)"\s*value="([^"]+)[A-Za-z&lt;>\s0-9"=\/]+&lt;tr>\s*&lt;td\s*colspan\s*=\s*[^>]+>([^&lt;]+)/gmi, ]; var mUpgrades; var upgrades = []; reUpgrades.forEach(function (reUpgrade) { while ((mUpgrades = reUpgrade.exec(html)) !== null) { if (mUpgrades[1].indexOf("Technological") &lt; 0) { upgrades.push({ title: mUpgrades[1], currentTitle: mUpgrades[2], upgradeName: helpers.stripHtml(mUpgrades[3]).replace(/\s+/g, ' ').trim(), currentLevel: mUpgrades[4], cost: mUpgrades[5], inputName: mUpgrades[6], inputValue: mUpgrades[7], }); } else { upgrades.push({ title: mUpgrades[1], currentTitle: mUpgrades[2], upgradeName: helpers.stripHtml(mUpgrades[3]).replace(/\s+/g, ' ').trim(), currentLevel: mUpgrades[7], cost: mUpgrades[4], inputName: mUpgrades[5], inputValue: mUpgrades[6], }); } } }); return { success: true, personnel: personnel, train: train, upgrades: upgrades, message: this.parseErrorMessageRed(html), turing: this.getTuring(html), // covertSkill: covertSkill, // conscription: conscription, // economicDevelopment: economicDevelopment, // technologicalDevelopment: technologicalDevelopment, }; }; parser.parseVerifyEmailError = function (html) { if (html === undefined || html === null || !html.length) return { success: false, error: "Empty page to parse" }; var re = /&lt;h2 style="color: red">([^&lt;]+)/gmi; var m = re.exec(html); if (m !== null) return m[1]; return ""; }; parser.parseMercenaries = function (html) { if (html === undefined || html === null || !html.length) return { success: false, error: "Empty page to parse" }; var reMercs = /&lt;td>([^&lt;]+)&lt;\/td>\s+&lt;td align="right">([^&lt;]+)&lt;\/td>\s+&lt;td align="right">([^&lt;]+)&lt;\/td>\s+.*name="([^"]+)".*value="([^"]+)/gmi; var mMercs; var hire = []; while ((mMercs = reMercs.exec(html)) !== null) { hire.push({ mercenaryType: mMercs[1], costPerUnitText: mMercs[2], costPerUnit: helpers.quantityFromString(mMercs[2], 0), quantityAvailableText: mMercs[3], quantityAvailable: helpers.quantityFromString(mMercs[3], 0), inputName: mMercs[4], inputValue: helpers.quantityFromString(mMercs[5]), }); } return { success: true, hire: hire, personnel: this.parsePersonnel(html), message: this.parseErrorMessageRed(html), turing: this.getTuring(html), }; }; parser.parseRecruit = function (html) { if (html === undefined || html === null || !html.length) return { success: false, error: "Empty page to parse" }; // Recruiting Preference is always there var reRecruitPref = /input\s*type\s*=\s*["']radio["']\s*name\s*=\s*["']\s*([^'"]+)["']\s*value\s*=\s*["']\s*([^'"]+)["']\s*([^>]*)>\s*([^&lt;]+)/gmi; var mRecruitPref; var recruitPref = []; var recruitPrefName = "clickPref"; var recruitPrefChecked = ""; while ((mRecruitPref = reRecruitPref.exec(html)) !== null) { recruitPrefName = mRecruitPref[1]; var checked = mRecruitPref[3].trim() == 'checked'; if(checked) recruitPrefChecked = mRecruitPref[4]; recruitPref.push({ name: mRecruitPref[4], value: mRecruitPref[2], checked: checked, }); } // Hidden fields var reHidden = /input\s*type=["']*hidden['"]*\s*name="([^"]+)"\s*value="([^"]+)/gmi; var mHiddenFields; var hiddenFields = {}; while ((mHiddenFields = reHidden.exec(html)) !== null) { hiddenFields[mHiddenFields[1]] = mHiddenFields[2]; } if(html.indexOf("There is no one left to click.") >= 0 ) { return { success: false, error: "There is no one left to click.", recruitPreference: recruitPrefChecked, recruitPreferences: recruitPref, recruitPreferencesInputName: recruitPrefName, }; } if (html.indexOf("&lt;h1>Invalid selection...&lt;/h1>") >= 0) { // Looks like you screwed up! var reChallenge = /action="recruit\.php".*\s*&lt;script\s*type="text\/javascript"\s*src="([^"]+)"/gmi; var mChallenge = reChallenge.exec(html); var challenge_url = mChallenge[1]; // response field var reChallengeFields = /textarea name="([^"]+)".*\s*&lt;input type="hidden" name="([^"]+)/gmi; var mChallengeField = reChallengeFields.exec(html); var challengeField = mChallengeField === null ? "recaptcha_challenge_field" : mChallengeField[1]; mChallengeField = reChallengeFields.exec(html); var challengeResponseField = mChallengeField === null ? "recaptcha_response_field" : mChallengeField[1]; return { success: false, error: "Invalid Selection", challenge_url: challenge_url, hiddenFields: hiddenFields, recruitPreference: recruitPrefChecked, recruitPreferences: recruitPref, recruitPreferencesInputName: recruitPrefName, challengeField: challengeField, challengeResponseField: challengeResponseField, }; } // Who are we recruiting for? var reUser = /recruit for &lt;b>&lt;a href="stats\.php\?id=([0-9]+)">\s*([^&lt;]+)\s*&lt;\/a>/gmi; var mUser = reUser.exec(html); var user = mUser === null ? { username: "", userid: -1, } : { username: mUser[2], userid: helpers.quantityFromString(mUser[1]), }; var reRecruitJavascript = /document\.write\(\s*[']*&lt;([^)]+)/gmi; var mRecruitJavascript = reRecruitJavascript.exec(html); if (mRecruitJavascript === null) return { success: false, error: "Could not parse the javascript to get the image", user: user, recruitPreference: recruitPrefChecked, recruitPreferences: recruitPref, recruitPreferencesInputName: recruitPrefName, }; var imageHtml = String(eval("String( '" + mRecruitJavascript[1] + ")")); var reRecruitImage = /img\s+src="([^"]+)/gmi; var mRecruitImage = reRecruitImage.exec(imageHtml); if (mRecruitImage === null) return { success: false, error: "Could not parse the image of the letter to type", user: user, recruitPreference: recruitPrefChecked, recruitPreferences: recruitPref, recruitPreferencesInputName: recruitPrefName, }; var image = mRecruitImage[1]; // Army Morale var reMorale = /Your Army's Morale:[^0-9-+]*([0-9-+]+)/gmi; var mMorale = reMorale.exec(html); var morale = (mMorale !== null) ? mMorale[1] : "???"; // Field name var reFieldName = /type="submit" value="[a-z]" name="([^"]+)/gmi; var mFieldName = reFieldName.exec(html); var fieldName = mFieldName === null ? "image_click_value" : mFieldName[1]; return { success: true, image: image, morale: morale, hiddenFields: hiddenFields, recruitPreference: recruitPrefChecked, recruitPreferences: recruitPref, recruitPreferencesInputName: recruitPrefName, fieldName: fieldName, user: user, }; }; parser.parseAttackLog = function (html) { if (html === undefined || html === null || !html.length) return { success: false, error: "Empty page to parse" }; var reOnYou = /&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>&lt;a\s*href="stats\.php\?id=([0-9]+)"\s*>([^&lt;]+)&lt;\/a>&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>&lt;a\s*href="detail\.php\?attack_id=([0-9]+)">([^&lt;]+)&lt;\/a>&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>/gmi; var mOnYou; var attacksOnYou = []; while ((mOnYou = reOnYou.exec(html)) !== null) { attacksOnYou.push({ time: mOnYou[1] + " " + mOnYou[2], enemy: { userid: mOnYou[3], username: mOnYou[4], }, type: mOnYou[5], attack_id: mOnYou[6], result: mOnYou[7], enemyLosses: mOnYou[8], yourLosses: mOnYou[9], damageByEnemy: mOnYou[10], damageByYou: mOnYou[11], }); } var reByYou = /&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>&lt;a\s*href="stats\.php\?id=([0-9]+)"\s*>([^&lt;]+)&lt;\/a>&lt;\/td>\s*&lt;td.*>&lt;a\s*href="detail\.php\?attack_id=([0-9]+)">([^&lt;]+)&lt;\/a>&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>/gmi; var mByYou; var attacksByYou = []; while ((mByYou = reByYou.exec(html)) !== null) { attacksByYou.push({ time: mByYou[1] + " " + mByYou[2], enemy: { userid: mByYou[3], username: mByYou[4], }, attack_id: mByYou[5], result: mByYou[6], enemyLosses: mByYou[7], yourLosses: mByYou[8], damageByEnemy: mByYou[9], damageByYou: mByYou[10], }); } var reTotal = />(.*) attacks total \| page (.*) of ([^&lt;]+)/gmi; var attacksOnYouTotal = 0, attacksByYouTotal = 0, attacksOnYouCurrentPage = 0, attacksOnYouMaxPage = 0, attacksByYouCurrentPage = 0, attacksByYouMaxPage = 0; var mTotal = reTotal.exec(html); if (mTotal !== null) { attacksOnYouTotal = mTotal[1]; attacksOnYouCurrentPage = mTotal[2]; attacksOnYouMaxPage = mTotal[3]; mTotal = reTotal.exec(html); if (mTotal !== null) { attacksByYouTotal = mTotal[1]; attacksByYouCurrentPage = mTotal[2]; attacksByYouMaxPage = mTotal[3]; } } // Retrieve the Back / Next pages so front-end doesn't have to compute var attacksByYouIndex = html.indexOf('Attacks by You'); var reBack = /attacklog\.php\?b_start=([0-9]*)&amp;o_start=([0-9]*)">[^ ]+ Back/gmi; var reNext = /attacklog\.php\?b_start=([0-9]*)&amp;o_start=([0-9]*)">Next/gmi; var mBack, mNext; var attacksOnYouBack ={}, attacksOnYouNext = {}; var attacksByYouBack = {}, attacksByYouNext = {}; var parameters = {}; while((mBack = reBack.exec(html)) !== null ){ parameters = { b_start: helpers.quantityFromString(mBack[1], 0), o_start: helpers.quantityFromString(mBack[2], 0), }; if(mBack.index &lt; attacksByYouIndex) attacksOnYouBack = parameters; else attacksByYouBack = parameters; } while((mNext = reNext.exec(html)) !== null ){ parameters = { b_start: helpers.quantityFromString(mNext[1], 0), o_start: helpers.quantityFromString(mNext[2], 0), }; if(mNext.index &lt; attacksByYouIndex) attacksOnYouNext = parameters; else attacksByYouNext = parameters; } return { success: true, attacksOnYou: { attacks: attacksOnYou, total: helpers.quantityFromString(attacksOnYouTotal), currentPage: helpers.quantityFromString(attacksOnYouCurrentPage), maxPage: helpers.quantityFromString(attacksOnYouMaxPage), backPage: attacksOnYouBack, nextPage: attacksOnYouNext, }, attacksByYou: { attacks: attacksByYou, total: helpers.quantityFromString(attacksByYouTotal), currentPage: helpers.quantityFromString(attacksByYouCurrentPage), maxPage: helpers.quantityFromString(attacksByYouMaxPage), backPage: attacksByYouBack, nextPage: attacksByYouNext, }, }; }; parser.parseIntelligence = function (html) { if (html === undefined || html === null || !html.length) return { success: false, error: "Empty page to parse" }; // Intercepted var reIntercepted = /&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td align="left">(.+)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>/gmi; var mIntercepted; var intercepted = []; while ((mIntercepted = reIntercepted.exec(html)) !== null) { intercepted.push({ time: mIntercepted[1] + ' ' + mIntercepted[2], enemy: helpers.parseUser(mIntercepted[3]), missionType: mIntercepted[4], numberOfSpies: mIntercepted[5], numberOfSpiesCaught: mIntercepted[6], }); } // Pages var rePages = />(.*) [operationsfiles]+ total \| page (.+) of ([^&lt;]+)/gmi; var interceptedTotal = 0, interceptedCurrent = 0, interceptedMax = 0, filesTotal = 0, filesCurrent = 0, filesMax = 0; var mTotal = rePages.exec(html); if (mTotal !== null) { interceptedTotal = helpers.quantityFromString(mTotal[1]); interceptedCurrent = helpers.quantityFromString(mTotal[2]); interceptedMax = helpers.quantityFromString(mTotal[3]); mTotal = rePages.exec(html); if (mTotal !== null) { filesTotal = helpers.quantityFromString(mTotal[1]); filesCurrent = helpers.quantityFromString(mTotal[2]); filesMax = helpers.quantityFromString(mTotal[3]); } } // Files var reFiles = /&lt;td align="left">(.*)&lt;\/td>\s*&lt;td.*>([^&lt;]+)&lt;\/td>\s*&lt;td.*>&lt;a\s*href="intelfile\.php\?asset_id=([0-9]+)/gmi; var mFiles; var files = []; while ((mFiles = reFiles.exec(html)) !== null) { files.push({ enemy: helpers.parseUser(mFiles[1]), numberOfReports: helpers.quantityFromString(mFiles[2]), asset_id: helpers.quantityFromString(mFiles[3]), }); } // Retrieve the Back / Next pages so front-end doesn't have to compute var attacksByYouIndex = html.indexOf('Attacks by You'); var reBack = /intel\.php\?b_start=([0-9]*)&amp;o_start=([0-9]*)">[^ ]+ Back/gmi; var reNext = /intel\.php\?b_start=([0-9]*)&amp;o_start=([0-9]*)">Next/gmi; var mBack, mNext; var interceptedBack ={}, interceptedNext = {}; var filesBack = {}, filesNext = {}; var parameters = {}; while((mBack = reBack.exec(html)) !== null ){ parameters = { b_start: helpers.quantityFromString(mBack[1], 0), o_start: helpers.quantityFromString(mBack[2], 0), }; if(mBack.index &lt; attacksByYouIndex) interceptedBack = parameters; else filesBack = parameters; } while((mNext = reNext.exec(html)) !== null ){ parameters = { b_start: helpers.quantityFromString(mNext[1], 0), o_start: helpers.quantityFromString(mNext[2], 0), }; if(mNext.index &lt; attacksByYouIndex) interceptedNext = parameters; else filesNext = parameters; } return { success: true, intercepted: { operations: intercepted, total: interceptedTotal, currentPage: interceptedCurrent, maxPage: interceptedMax, backPage: interceptedBack, nextPage: interceptedNext, }, files: { reports: files, total: filesTotal, currentPage: filesCurrent, maxPage: filesMax, backPage: filesBack, nextPage: filesNext, } }; }; parser.parseBattleReport = function (html, location, userid) { // attack_id from location var attackId = -1; if(location!==undefined &amp;&amp; location!==null &amp;&amp; location.length){ var reAttackId = /attack_id=([0-9]+)/; var mAttackId = reAttackId.exec(location); attackId = (mAttackId!==null) ? helpers.quantityFromString(mAttackId[1]) : -1; } if (html === undefined || html === null || !html.length) return { success: false, error: "Empty page to parse", userid: userid === undefine