User:CLT20RecordsUpdateBot/Source/StatsUpdateFunctions.php

<?php

require 'translate.php';
include 'CricinfoDataParser.php';

# HELPER FUNCTIONS FOR THE STAT UPDATE FUNCTIONS

function SUF_generatePlayerSortNameLink($name, $uri = '', $flag = false) {
    # Generates a {{sortname}} template from a Cricinfo player name, optionally with a flag of that player's country
    global $CricinfoPlayerNameTranslationTable;

    # If the unique ID from the URI given exists in the table, use it, otherwise use the name
    preg_match('%/content(?:/.*?)?/player/(\d+)\.html$%', $uri, $idMatch);
    $index = isset($CricinfoPlayerNameTranslationTable[@$idMatch[1]]) ? $idMatch[1] : $name;

    $playerdata = @$CricinfoPlayerNameTranslationTable[$index];

    if ( ! $playerdata ) {
        throw new Exception("Player name does not exist in translation table: {$name}");
    }

    $result = "{{sortname|1={$playerdata['first']}|2={$playerdata['last']}"
              . (@$playerdata['page'] ? "|3={$playerdata['page']}" : '') . (@$playerdata['sort'] ? "|4={$playerdata['sort']}" : '') . "}}";

    if ( $flag && isset($playerdata['country']) ) {
        $result = "{{flagicon|{$playerdata['country']}}} " . $result;
    }

    return $result;
}

function SUF_generateTeamNameLink($name, $useFlag = false, $useAbbr = false) {

    global $CricinfoTeamNameTranslationTable;
    $teamdata = @$CricinfoTeamNameTranslationTable[$name];

    if ( ! $teamdata ) {
        throw new Exception("Team name does not exist in translation table: {$name}");
    }
    
    $linkText = '';
    
    if ( $useFlag && isset($teamdata['country']) ) {
        $linkText .= "{{flagicon|{$teamdata['country']}}} ";
    }
    
    if ( $useAbbr && $teamdata['name'] != @$teamdata['abbr'] ) {
        $linkText .= '[[' . (@$teamdata['page'] ?: $teamdata['name']) . "|{$teamdata['abbr']}]]";
    }
    else {
        $linkText .= '[[' . (@$teamdata['page'] ? $teamdata['page'] . '|' : '') . "{$teamdata['name']}]]";
    }
    
    return $linkText;
    
}

function SUF_generateTeamNameLinksList($names) {

    if ( strlen(implode(', ', $names)) > 30 ) $useAbbr = true;
    else $useAbbr = false;
    
    $links = [];
    foreach ( $names as $name ) $links[] = SUF_generateTeamNameLink($name, false, $useAbbr);
    
    return implode(', ', $links);
    
}

function SUF_generateGroundNameLink($uri) {
    # Generate wikitext consisting of the ground name and ___location from a Cricinfo profile page URI
    global $CricinfoGroundNameTranslationTable;

    preg_match('%/content(?:/.*?)?/ground/(\d+)\.html$%', $uri, $idMatch);
    $id = $idMatch[1];
    $grounddata = @$CricinfoGroundNameTranslationTable[$id];

    if ( ! $grounddata ) {
        throw new Exception("Ground ID not found in translation table: {$id}");
    }

    return "'''[[{$grounddata['___location']}]]''' – [[{$grounddata['name']}]]";
}

function SUF_generateWikiTableRow($data) {
    # Generates a wiki text table row from the function's arguments
    return '| ' . implode(' || ', func_get_args());
}

function SUF_encodeWikiTemplates($text) {
    # Replaces characters in templates which conflict with table and header syntax, with HTML entities.
    # These will be decoded later.
    $text = preg_replace_callback('/\{\{(?:[^\{\}]++|(?<!\{)\{|\}(?!\})|(?R))*?\}\}/u',
        function($match) {
            return str_replace(['&', '|', '!', '='], ['&amp;', '&#124;', '&#33;', '&#61;'], $match[0]);
        } , $text);
    return $text;
}

# Regular expression to capture the body of a wikitext table
# Note that newlines in the header region of a table are not allowed as setting the s modifier globally causes unexpected results
$wikiTableBodyRegex = '/^\{\|.*?\n(?:(?:\!|\|\-).*?\n)*\|\-.*?\n((?:\|\-?(?s).*?(?-s)\n)*?)\|\}/mu';

# The default limit (maximum number of rows to be returned from the parsed data) for all functions, unless explicitly overridden in the function itself.
$defaultLimit = 5;

# Load the match results (they are requuired by some of the functions)
# The results of earlier seasons are stored locally in old_match_results.json to reduce time and server load. Only the current season's results need to be fetched.
$matchResultsParser = new CricinfoDataParser();


$matchResultsParser->load('http://stats.espncricinfo.com/ci/engine/records/team/match_results.html?class=;id=2013/14;trophy=120;type=season');
if ( $matchResultsParser->loadFailed ) {
    $currentMatchResults = [];
}
else {
    $currentMatchResults = $matchResultsParser->parse(
       0,
       [ 'C_TEAM1', 'C_TEAM2', 'C_WINNER', 'C_MARGIN', 'C_GROUND', 'C_MATCH_DATE', 'C_SCORECARD' ],
       true
    );
}

$MatchResults = array_merge(
    json_decode(file_get_contents('old_match_results.json'), true),
    $currentMatchResults
);
unset($matchResultsParser, $currentMatchResults);


$StatsUpdateFunctions = [

    # ---------- STATS UPDATE FUNCTIONS START HERE ----------

    # This uses PHP 5.3 syntax, older versions need to use create_function()
    # Not using data-sort-value attributes as sort keys for now, as HTML5 is not supported by some browsers. Use the sorting templates instead.
    # Functions should return true on success, false on failure.


    # TEAM RECORDS

    'TEAM_HIGHEST_TOTALS'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n===Highest totals===", strpos($PageText, "\n==Team records=="));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: TEAM_HIGHEST_TOTALS', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/champions-league-twenty20-2013/engine/records/team/highest_innings_totals.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_TEAM', 'C_SCORE', 'C_OVERS', 'C_RUN_RATE', 'C_INNINGS', 6 => 'C_OPPOSITION', 'C_GROUND', 'C_DATE', 'C_SCORECARD' ],
            true,
            $defaultLimit,
            [ 'C_SCORE', 'C_RUN_RATE', 'C_DATE' ],
            CricinfoDataParser::SORT_DESCENDING
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                '{{ntsh|{{#expr:' . (explode('/', $row['C_SCORE'])[0] + 1) . '+1/' . (((strpos($row['C_SCORE'], '/') !== false) ? explode('/', $row['C_SCORE'])[1] : 10) + 1) . '}}}}' . "'''{$row['C_SCORE']}'''",  # Add 1 to both runs and wickets in the sort key to avoid a division by zero
                'style="text-align:left"|' . SUF_generateTeamNameLink($row['C_TEAM'], true),
                'style="text-align:left"|' . SUF_generateTeamNameLink(substr($row['C_OPPOSITION'], 2), true),
                $row['C_OVERS'],
                $row['C_RUN_RATE'],
                $row['C_INNINGS'],
                '[[' . date('Y', strtotime($row['C_DATE'])) . ' Champions League Twenty20|' . date('Y', strtotime($row['C_DATE'])) . ']]',
                'style="text-align:left; font-size:85%"|' . SUF_generateGroundNameLink($row['C_GROUND:href'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        # Since the table body regex catches the newline before the ending "|}", add it to the replacement string to avoid breaking the table syntax

        return true;
    },


    'TEAM_LOWEST_TOTALS'  =>  function() {
        
        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n===Lowest totals===", strpos($PageText, "\n==Team records=="));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: TEAM_LOWEST_TOTALS', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/champions-league-twenty20-2013/engine/records/team/lowest_innings_totals.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_TEAM', 'C_SCORE', 'C_OVERS', 'C_RUN_RATE', 'C_INNINGS', 6 => 'C_OPPOSITION', 'C_GROUND', 'C_DATE', 'C_SCORECARD' ],
            true,
            $defaultLimit,
            [ 'C_SCORE', 'C_RUN_RATE', 'C_DATE' ],
            CricinfoDataParser::SORT_ASCENDING,
            [ 'C_DATE' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                '{{ntsh|{{#expr:' . (explode('/', $row['C_SCORE'])[0] + 1) . '+1/' . (((strpos($row['C_SCORE'], '/') !== false) ? explode('/', $row['C_SCORE'])[1] : 10) + 1) . '}}}}' . "'''{$row['C_SCORE']}'''",
                'style="text-align:left"|' . SUF_generateTeamNameLink($row['C_TEAM'], true),
                'style="text-align:left"|' . SUF_generateTeamNameLink(substr($row['C_OPPOSITION'], 2), true),
                $row['C_OVERS'],
                $row['C_RUN_RATE'],
                $row['C_INNINGS'],
                '[[' . date('Y', strtotime($row['C_DATE'])) . ' Champions League Twenty20|' . date('Y', strtotime($row['C_DATE'])) . ']]',
                'style="text-align:left; font-size:85%"|' . SUF_generateGroundNameLink($row['C_GROUND:href'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;

    },


    'TEAM_HIGHEST_MATCH_AGGREGATES'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit, $MatchResults;

        $tablePosition = strpos($PageText, "\n===Highest match aggregates===", strpos($PageText, "\n==Team records=="));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: TEAM_HIGHEST_MATCH_AGGREGATES', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/champions-league-twenty20-2013/engine/records/team/highest_match_aggregates.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_TEAM1', 'C_TEAM2', 'C_RUNS', 'C_WICKETS', 'C_OVERS', 'C_RUN_RATE', 7 => 'C_GROUND', 'C_DATE', 'C_SCORECARD' ],
            true,
            $defaultLimit,
            [ 'C_RUNS', 'C_RUN_RATE', 'C_WICKETS', 'C_DATE' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_WICKETS' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {

            # Find the result of the match to determine the winner
            preg_match('%engine/match/(\d+)\.html$%', $row['C_SCORECARD:href'], $idMatch);
            $thisMatchResult = array_filter($MatchResults,
                function($result) use ($idMatch) {
                    return preg_match('%engine/match/' . preg_quote($idMatch[1], '%') . '\.html$%', $result['C_SCORECARD:href']);
                }
            );
            
            $thisMatchResult = $thisMatchResult[array_keys($thisMatchResult)[0]];
            # Trigger a warning if a tied match is found (since super over results are not given, winner and loser columns may be mismatched in the result)
            if ( $thisMatchResult['C_WINNER'] == 'tied' ) {
                trigger_error("Tied match found when attempting to determine winner (scorecard URI: {$thisMatchResult['C_SCORECARD:href']}), winner and loser columns may be mismatched", E_USER_WARNING);
            }

            $newTableRows[] = SUF_generateWikiTableRow(
                "'''{$row['C_RUNS']}'''",
                'style="text-align:left"|' . SUF_generateTeamNameLink( ($thisMatchResult['C_WINNER'] == $row['C_TEAM1']) ? $row['C_TEAM1'] : $row['C_TEAM2'], true ),
                'style="text-align:left"|' . SUF_generateTeamNameLink( ($thisMatchResult['C_WINNER'] == $row['C_TEAM1']) ? $row['C_TEAM2'] : $row['C_TEAM1'], true ),
                $row['C_OVERS'],
                $row['C_RUN_RATE'],
                $row['C_WICKETS'],
                '[[' . date('Y', strtotime($row['C_DATE'])) . ' Champions League Twenty20|' . date('Y', strtotime($row['C_DATE'])) . ']]',
                'style="text-align:left; font-size:85%"|' . SUF_generateGroundNameLink($row['C_GROUND:href'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },


    'TEAM_LOWEST_MATCH_AGGREGATES'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit, $MatchResults;

        $tablePosition = strpos($PageText, "\n===Lowest match aggregates===", strpos($PageText, "\n==Team records=="));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: TEAM_LOWEST_MATCH_AGGREGATES', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/champions-league-twenty20-2013/engine/records/team/lowest_match_aggregates.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_TEAM1', 'C_TEAM2', 'C_RUNS', 'C_WICKETS', 'C_OVERS', 'C_RUN_RATE', 7 => 'C_GROUND', 'C_DATE', 'C_SCORECARD' ],
            true,
            $defaultLimit,
            [ 'C_RUNS', 'C_RUN_RATE', 'C_WICKETS', 'C_DATE' ],
            CricinfoDataParser::SORT_ASCENDING,
            [ 'C_WICKETS', 'C_DATE' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            preg_match('%engine/match/(\d+)\.html$%', $row['C_SCORECARD:href'], $idMatch);
            $thisMatchResult = array_filter($MatchResults,
                function($result) use ($idMatch) {
                    return preg_match('%engine/match/' . preg_quote($idMatch[1], '%') . '\.html$%', $result['C_SCORECARD:href']);
                }
            );
            
            $thisMatchResult = $thisMatchResult[array_keys($thisMatchResult)[0]];
            if ( $thisMatchResult['C_WINNER'] == 'tied' ) {
                trigger_error("Tied match found when attempting to determine winner (scorecard URI: {$thisMatchResult['C_SCORECARD:href']}), winner and loser columns may be mismatched", E_USER_WARNING);
            }

            $newTableRows[] = SUF_generateWikiTableRow(
                "'''{$row['C_RUNS']}'''",
                'style="text-align:left"|' . SUF_generateTeamNameLink( ($thisMatchResult['C_WINNER'] == $row['C_TEAM1']) ? $row['C_TEAM1'] : $row['C_TEAM2'], true ),
                'style="text-align:left"|' . SUF_generateTeamNameLink( ($thisMatchResult['C_WINNER'] == $row['C_TEAM1']) ? $row['C_TEAM2'] : $row['C_TEAM1'], true ),
                $row['C_OVERS'],
                $row['C_RUN_RATE'],
                $row['C_WICKETS'],
                '[[' . date('Y', strtotime($row['C_DATE'])) . ' Champions League Twenty20|' . date('Y', strtotime($row['C_DATE'])) . ']]',
                'style="text-align:left; font-size:85%"|' . SUF_generateGroundNameLink($row['C_GROUND:href'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },


    'TEAM_LARGEST_VICTORIES'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n===Largest victories===", strpos($PageText, "\n==Team records=="));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: TEAM_LARGEST_VICTORIES', E_USER_WARNING);
            return false;
        }
        
        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/champions-league-twenty20-2013/engine/records/team/largest_margins.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }
        
        $parseResult = $parser->parse(
            0,
            [ 'C_WINNER', 'C_MARGIN', 'C_TARGET', 4 => 'C_OPPOSITION', 'C_GROUND', 'C_DATE', 'C_SCORECARD' ],
            true,
            $defaultLimit,
            [ 'C_MARGIN', 'C_TARGET', 'C_DATE' ],
            CricinfoDataParser::SORT_DESCENDING
        );
        
        $newTableRows = [];
        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                "'''{{nts|" . ((int) $row['C_MARGIN']) . '}} run' . (($row['C_MARGIN'] > 1) ? 's' : '') . "'''",
                'style="text-align:left"|' . SUF_generateTeamNameLink($row['C_WINNER'], true),
                $row['C_TARGET'],
                'style="text-align:left"|' . SUF_generateTeamNameLink(substr($row['C_OPPOSITION'], 2), true),
                '[[' . date('Y', strtotime($row['C_DATE'])) . ' Champions League Twenty20|' . date('Y', strtotime($row['C_DATE'])) . ']]',
                'style="text-align:left; font-size:85%"|' . SUF_generateGroundNameLink($row['C_GROUND:href'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
        
    },

    

    'MOST_MATCHES'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n===Most matches===", strpos($PageText, "\n==Individual records=="));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: MOST_MATCHES', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/individual/most_matches_career.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_SPAN', 'C_MATCHES', 'C_RUNS', 'C_HIGH_SCORE', 'C_BATTING_AVERAGE', 'C_100', 'C_WICKETS', 'C_BEST_BOWLING', 'C_BOWLING_AVERAGE', 'C_5_WICKETS', 'C_CATCHES', 'C_STUMPINGS' ],
            true,
            5,
            [ 'C_MATCHES', 'C_NAME' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true),
                $row['C_MATCHES'],
                'style="text-align:left; font-size:85%"|' . SUF_generateTeamNameLinksList( explode(', ', substr($row['NOTE'], 1, -1)) ),
                str_replace('-', '&#8211;', $row['C_SPAN'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },
    
    
    # INDIVIDUAL RECORDS (BATTING)

    'BATTING_MOST_RUNS'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Most runs====", strpos($PageText, "\n===Batting Records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: BATTING_MOST_RUNS', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/batting/most_runs_career.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_SPAN', 'C_MATCHES', 'C_INNINGS', 'C_NOT_OUT', 'C_RUNS', 'C_HIGH_SCORE', 'C_AVERAGE', 'C_BALLS', 'C_STRIKE_RATE', 'C_100', 'C_50', 'C_0', 'C_4', 'C_6' ],
            true,
            $defaultLimit,
            [ 'C_RUNS', 'C_AVERAGE', 'C_STRIKE_RATE', 'C_HIGH_SCORE', 'C_100', 'C_50', 'C_6', 'C_4', 'C_NAME' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink( $row['C_NAME'], $row['C_NAME:href'], true ),
                $row['C_INNINGS'],
                $row['C_RUNS'],
                $row['C_100'] + $row['C_50'],
                $row['C_AVERAGE'],
                'style="text-align:left; font-size:85%"|' . SUF_generateTeamNameLinksList( explode(', ', substr($row['NOTE'], 1, -1)) ),
                str_replace('-', '&#8211;', $row['C_SPAN'])  # Replace hyphen with en-dash
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },


    'BATTING_BEST_STRIKE_RATE'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Best strike rates====", strpos($PageText, "\n===Batting Records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: BATTING_BEST_STRIKE_RATE', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/batting/highest_career_strike_rate.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_SPAN', 'C_MATCHES', 'C_INNINGS', 'C_NOT_OUT', 'C_RUNS', 'C_HIGH_SCORE', 'C_AVERAGE', 'C_BALLS', 'C_STRIKE_RATE', 'C_100', 'C_50', 'C_0', 'C_4', 'C_6' ],
            true,
            $defaultLimit,
            [ 'C_STRIKE_RATE', 'C_RUNS', 'C_AVERAGE', 'C_HIGH_SCORE', 'C_100', 'C_50', 'C_6', 'C_4', 'C_NAME' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME' ],
            function($row) {
                return $row['C_BALLS'] >= 100;
            }
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true)
                    . ' <span style="font-size:85%">(' . SUF_generateTeamNameLinksList( explode(', ', substr($row['NOTE'], 1, -1)) ) . ')</span>',
                $row['C_STRIKE_RATE'],
                str_replace('-', '&#8211;', $row['C_SPAN'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },


    'BATTING_BEST_AVERAGE'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Highest batting averages====", strpos($PageText, "\n===Batting Records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: BATTING_BEST_AVERAGE', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/batting/highest_career_batting_average.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_SPAN', 'C_MATCHES', 'C_INNINGS', 'C_NOT_OUT', 'C_RUNS', 'C_HIGH_SCORE', 'C_AVERAGE', 'C_BALLS', 'C_STRIKE_RATE', 'C_100', 'C_50', 'C_0', 'C_4', 'C_6' ],
            true,
            $defaultLimit,
            [ 'C_AVERAGE', 'C_RUNS', 'C_STRIKE_RATE', 'C_HIGH_SCORE', 'C_100', 'C_50', 'C_6', 'C_4', 'C_NAME' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME' ],
            function($row) {
                return $row['C_INNINGS'] >= 10;
            }
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true)
                    . ' <span style="font-size:85%">(' . SUF_generateTeamNameLinksList( explode(', ', substr($row['NOTE'], 1, -1)) ) . ')</span>',
                $row['C_INNINGS'],
                $row['C_AVERAGE'],
                str_replace('-', '&#8211;', $row['C_SPAN'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },


    'BATTING_HIGHEST_SCORE'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Highest scores====", strpos($PageText, "\n===Batting Records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: BATTING_HIGHEST_SCORE', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/batting/most_runs_innings.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_RUNS', 'C_BALLS', 'C_4', 'C_6', 'C_STRIKE_RATE', 7 => 'C_TEAM', 'C_OPPOSITION', 'C_GROUND', 'C_DATE', 'C_SCORECARD' ],
            true,
            $defaultLimit,
            [ 'C_RUNS', 'C_STRIKE_RATE', 'C_6', 'C_4', 'C_NAME', 'C_DATE' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true)
                    . ' <span style="font-size:85%">(' . SUF_generateTeamNameLink($row['C_TEAM']) . ')</span>',
                '{{nts|' . ((int) $row['C_RUNS']) . '}}' . ((strpos($row['C_RUNS'], '*') !== false) ? '*' : '') . "({$row['C_BALLS']})",
                date( 'Y', strtotime($row['C_DATE']) )
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },

    
    'BATTING_MOST_SIXES'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Most sixes====", strpos($PageText, "\n===Batting Records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: BATTING_MOST_SIXES', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/batting/most_sixes_career.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_SPAN', 'C_MATCHES', 'C_INNINGS', 'C_NOT_OUT', 'C_RUNS', 'C_HIGH_SCORE', 'C_AVERAGE', 'C_BALLS', 'C_STRIKE_RATE', 'C_100', 'C_50', 'C_0', 'C_4', 'C_6' ],
            true,
            $defaultLimit,
            [ 'C_6', 'C_RUNS', 'C_AVERAGE', 'C_STRIKE_RATE', 'C_HIGH_SCORE', 'C_100', 'C_50', 'C_4', 'C_NAME' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true)
                    . ' <span style="font-size:85%">(' . SUF_generateTeamNameLinksList( explode(', ', substr($row['NOTE'], 1, -1)) ) . ')</span>',
                $row['C_6'],
                str_replace('-', '&#8211;', $row['C_SPAN'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },
    
    
    'BATTING_MOST_FIFTIES'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Most fifties====", strpos($PageText, "\n===Batting Records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: BATTING_MOST_FIFTIES', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/batting/most_fifties_career.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_SPAN', 'C_MATCHES', 'C_INNINGS', 'C_NOT_OUT', 'C_RUNS', 'C_HIGH_SCORE', 'C_AVERAGE', 'C_BALLS', 'C_STRIKE_RATE', 'C_100', 'C_50', 'C_50+', 'C_0', 'C_4', 'C_6' ],
            true,
            $defaultLimit,
            [ 'C_50+', 'C_100', 'C_50', 'C_RUNS', 'C_AVERAGE', 'C_STRIKE_RATE', 'C_HIGH_SCORE', 'C_6', 'C_4', 'C_NAME' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true)
                    . ' <span style="font-size:85%">(' . SUF_generateTeamNameLinksList( explode(', ', substr($row['NOTE'], 1, -1)) ) . ')</span>',
                $row['C_INNINGS'],
                $row['C_50+'],
                $row['C_AVERAGE'],
                $row['C_STRIKE_RATE'],
                str_replace('-', '&#8211;', $row['C_SPAN'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },


    # INDIVIDUAL RECORDS (BOWLING)

    'BOWLING_MOST_WICKETS'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Most wickets====", strpos($PageText, "\n===Bowling Records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: BOWLING_MOST_WICKETS', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/bowling/most_wickets_career.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_SPAN', 'C_MATCHES', 'C_INNINGS', 'C_OVERS', 'C_MAIDENS', 'C_RUNS', 'C_WICKETS', 'C_BEST_BOWLING', 'C_AVERAGE', 'C_ECONOMY_RATE', 'C_STRIKE_RATE', 'C_4_WICKETS', 'C_5_WICKETS' ],
            true,
            $defaultLimit,
            [ 'C_WICKETS', 'C_ECONOMY_RATE', 'C_AVERAGE', 'C_STRIKE_RATE', 'C_BEST_BOWLING', 'C_5_WICKETS', 'C_4_WICKETS', 'C_NAME' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME', 'C_ECONOMY_RATE', 'C_STRIKE_RATE', 'C_AVERAGE' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true),
                $row['C_MATCHES'],
                $row['C_WICKETS'],
                '{{ntsh|{{#expr:' . (explode('/', $row['C_BEST_BOWLING'])[0] + 1) . '+1/' . (explode('/', $row['C_BEST_BOWLING'])[1] + 1) . '}}}}' . $row['C_BEST_BOWLING'],
                $row['C_ECONOMY_RATE'],
                'style="text-align:left; font-size:85%"|' . SUF_generateTeamNameLinksList( explode(', ', substr($row['NOTE'], 1, -1)) ),
                str_replace('-', '&#8211;', $row['C_SPAN'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },


    'BOWLING_BEST_ECONOMY_RATE'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Best economy rates====", strpos($PageText, "\n===Bowling Records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: BOWLING_BEST_ECONOMY_RATE', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/bowling/best_career_economy_rate.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_SPAN', 'C_MATCHES', 'C_OVERS', 'C_MAIDENS', 'C_RUNS', 'C_WICKETS', 'C_BEST_BOWLING', 'C_AVERAGE', 'C_ECONOMY_RATE', 'C_STRIKE_RATE', 'C_4_WICKETS', 'C_5_WICKETS' ],
            true,
            $defaultLimit,
            [ 'C_ECONOMY_RATE', 'C_WICKETS', 'C_AVERAGE', 'C_STRIKE_RATE', 'C_BEST_BOWLING', 'C_5_WICKETS', 'C_4_WICKETS', 'C_NAME' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME', 'C_ECONOMY_RATE', 'C_STRIKE_RATE', 'C_AVERAGE' ],
            function($row) {
                return $row['C_OVERS'] >= 10;
            }
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true)
                    . ' <span style="font-size:85%">(' . SUF_generateTeamNameLinksList( explode(', ', substr($row['NOTE'], 1, -1)) ) . ')</span>',
                $row['C_ECONOMY_RATE'],
                str_replace('-', '&#8211;', $row['C_SPAN'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },


    'BOWLING_BEST_FIGURES_INNINGS'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Best bowling figures====", strpos($PageText, "\n===Bowling Records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: BOWLING_BEST_FIGURES_INNINGS', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/bowling/best_figures_innings.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_OVERS', 'C_MAIDENS', 'C_RUNS', 'C_WICKETS', 'C_ECONOMY_RATE', 7 => 'C_TEAM', 'C_OPPOSITION', 'C_GROUND', 'C_DATE', 'C_SCORECARD' ],
            true,
            $defaultLimit,
            [ 'C_WICKETS', 'C_RUNS', 'C_ECONOMY_RATE', 'C_MAIDENS', 'C_NAME', 'C_DATE' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME', 'C_RUNS', 'C_ECONOMY_RATE' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true)
                    . ' <span style="font-size:85%">(' . SUF_generateTeamNameLink($row['C_TEAM']) . ')</span>',
                '{{ntsh|{{#expr:' . ($row['C_WICKETS'] + 1) . '+1/' . ($row['C_RUNS'] + 1) . '}}}}' . "'''{$row['C_WICKETS']}/{$row['C_RUNS']}'''",
                date( 'Y', strtotime($row['C_DATE']) )
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },
    
    'BOWLING_BEST_ECONOMY_INNINGS'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Best economy rates in an innings====", strpos($PageText, "\n===Bowling Records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: BOWLING_BEST_ECONOMY_INNINGS', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/bowling/best_economy_rate_innings.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_OVERS', 'C_MAIDENS', 'C_RUNS', 'C_WICKETS', 'C_ECONOMY_RATE', 7 => 'C_TEAM', 'C_OPPOSITION', 'C_GROUND', 'C_DATE', 'C_SCORECARD' ],
            true,
            $defaultLimit,
            [ 'C_ECONOMY_RATE', 'C_WICKETS', 'C_RUNS', 'C_MAIDENS', 'C_NAME', 'C_DATE' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME', 'C_RUNS', 'C_ECONOMY_RATE' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true)
                    . ' <span style="font-size:85%">(' . SUF_generateTeamNameLink($row['C_TEAM']) . ')</span>',
                '{{ntsh|{{#expr:' . ($row['C_WICKETS'] + 1) . '+1/' . ($row['C_RUNS'] + 1) . '}}}}' . "'''{$row['C_WICKETS']}/{$row['C_RUNS']}'''",
                $row['C_ECONOMY_RATE'],
                'style="text-align:left"|' . SUF_generateTeamNameLink( substr($row['C_OPPOSITION'], 2) ),
                date( 'Y', strtotime($row['C_DATE']) )
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },


    # INDIVIDUAL RECORDS (FIELDING AND WICKETKEEPING)

    'FIELDING_MOST_DISMISSALS_KEEPER'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Most dismissals====", strpos($PageText, "\n===Wicketkeeping and fielding records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: FIELDING_MOST_DISMISSALS_KEEPER', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/keeping/most_dismissals_career.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_SPAN', 'C_MATCHES', 'C_INNINGS', 'C_DISMISSALS', 'C_CATCHES', 'C_STUMPINGS', 'C_MAX_DISMISSALS_INNINGS', 'C_AVERAGE_DISMISSALS_INNINGS' ],
            true,
            $defaultLimit,
            [ 'C_DISMISSALS', 'C_AVERAGE_DISMISSALS_INNINGS', 'C_MAX_DISMISSALS_INNINGS', 'C_NAME' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true)
                    . ' <span style="font-size:85%">(' . SUF_generateTeamNameLinksList( explode(', ', substr($row['NOTE'], 1, -1)) ) . ')</span>',
                $row['C_DISMISSALS'],
                str_replace('-', '&#8211;', $row['C_SPAN'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },


    'FIELDING_MOST_CATCHES_NON_KEEPER'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Most catches====", strpos($PageText, "\n===Wicketkeeping and fielding records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: FIELDING_MOST_CATCHES_NON_KEEPER', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/fielding/most_catches_career.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_SPAN', 'C_MATCHES', 'C_INNINGS', 'C_CATCHES', 'C_MAX_CATCHES_INNINGS', 'C_AVERAGE_CATCHES_INNINGS' ],
            true,
            $defaultLimit,
            [ 'C_CATCHES', 'C_AVERAGE_CATCHES_INNINGS', 'C_MAX_CATCHES_INNINGS', 'C_NAME' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true)
                    . ' <span style="font-size:85%">(' . SUF_generateTeamNameLinksList( explode(', ', substr($row['NOTE'], 1, -1)) ) . ')</span>',
                $row['C_CATCHES'],
                str_replace('-', '&#8211;', $row['C_SPAN'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },

    
    'FIELDING_MOST_CATCHES_INNINGS_NON_KEEPER'  =>  function() {

        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Most catches in an innings====", strpos($PageText, "\n===Wicketkeeping and fielding records===", strpos($PageText, "\n==Individual records==")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: FIELDING_MOST_CATCHES_INNINGS_NON_KEEPER', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/fielding/most_catches_innings.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 'C_CATCHES', 'C_INNINGS', 4 => 'C_TEAM', 'C_OPPOSITION', 'C_GROUND', 'C_DATE', 'C_SCORECARD' ],
            true,
            $defaultLimit,
            [ 'C_CATCHES', 'C_NAME', 'C_DATE' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true)
                    . ' <span style="font-size:85%">(' . SUF_generateTeamNameLink($row['C_TEAM']) . ')</span>',
                $row['C_CATCHES'],
                'style="text-align:left"|' . SUF_generateTeamNameLink( substr($row['C_OPPOSITION'], 2) ),
                date( 'Y', strtotime($row['C_DATE']) )
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },
    
    
    'MOST_MATCHES_UMPIRE'  =>  function() {
        
        global $PageText, $wikiTableBodyRegex, $defaultLimit;

        $tablePosition = strpos($PageText, "\n====Most matches as an umpire====", strpos($PageText, "\n===Umpire records===", strpos($PageText, "\n==Individual records")));
        if ( $tablePosition === false ) {
            trigger_error('Cannot find section header for function: MOST_MATCHES_UMPIRE', E_USER_WARNING);
            return false;
        }

        $parser = new CricinfoDataParser();
        $parser->load('http://stats.espncricinfo.com/ci/engine/records/individual/most_matches_umpire.html?id=120;type=trophy');
        if ( $parser->loadFailed ) {
            return false;
        }

        $parseResult = $parser->parse(
            0,
            [ 'C_NAME', 2 => 'C_SPAN', 'C_MATCHES' ],
            true,
            5,
            [ 'C_MATCHES', 'C_NAME' ],
            CricinfoDataParser::SORT_DESCENDING,
            [ 'C_NAME' ]
        );

        $newTableRows = [];

        foreach ( $parseResult as $row ) {
            $newTableRows[] = SUF_generateWikiTableRow(
                'style="text-align:left"|' . SUF_generatePlayerSortNameLink($row['C_NAME'], $row['C_NAME:href'], true),
                $row['C_MATCHES'],
                str_replace('-', '&#8211;', $row['C_SPAN'])
            );
        }

        preg_match($wikiTableBodyRegex, $PageText, $tableMatch, 0, $tablePosition);

        $PageText = str_replace($tableMatch[1], SUF_encodeWikiTemplates(implode("\n|-\n", $newTableRows)) . "\n", $PageText);
        return true;
    },


    # ---------- END OF STATS UPDATE FUNCTIONS ----------

];

?>