import * as cheerio from "cheerio" ; import * as querystring from "querystring" ; import { ParsedUrlQuery } from "querystring" ; import { BoxingBoutOutcome } from "./boxrec-pages/event/boxrec.event.constants" ; param { string } fraction returns { number } export function convertFractionsToNumber ( fraction ) { switch ( fraction ) { case "&#xBC;" : case "¼" : return .25 ; case "&#xBD;" : case "½" : return .5 ; case "&#xBE;" : case "¾" : return .75 ; default : return 0 ; } } param { string } str returns { string } export function trimRemoveLineBreaks ( str ) { return str . trim ( ) . replace ( /(?:\r\n|\r|\n)/g , "" ) . replace ( /\s{2,}/g , " " ) ; } param { string } str example returns { string } export function changeToCamelCase ( str ) { const camelCaseStr = str . replace ( /\s(\w)/g , x => x [ 1 ] . toUpperCase ( ) ) ; return `${ camelCaseStr . charAt ( 0 ) . toLowerCase ( ) }${ camelCaseStr . slice ( 1 ) }` ; } param $ param tableColumnsArr param columnHeaderText param returnHTML export function getColumnDataByColumnHeader ( $ , tableColumnsArr , columnHeaderText , returnHTML = true ) { const tableEl = $ ( $ ( "<div>" ) . append ( $ ( "table" ) . clone ( ) ) . html ( ) ) ; const idx = tableColumnsArr . findIndex ( item => item === columnHeaderText ) ; if ( idx > - 1 ) { const el = tableEl . find ( `tr:nth-child(1) td:nth-child(${ idx + 1 })` ) ; if ( returnHTML ) { const html = el . html ( ) ; if ( html ) { return trimRemoveLineBreaks ( html ) ; } return "" ; } return trimRemoveLineBreaks ( el . text ( ) ) ; } return "" ; } /**
 * Common table header text that is used to quickly find data if the column number changes or the header name changes
 * synthetic headers don't actually exist but are used to classify a column as that type of data
 */ export enum BoxrecCommonTableHeader { age = "age" , career = "career" , date = "date" , day = "day" , debut = "debut" , division = "division" , fighter = "fighter" , firstLast6 = "firstLast6" , firstFighterWeight = "firstFighterWeight" , firstRating = "firstRating" , links = "links" , location = "location" , miles = "miles" , name = "name" , outcome = "outcome" , opponent = "opponent" , outcomeByWayOf = "outcomeByWayOf" , points = "points" , rating = "rating" , residence = "residence" , result = "result" , rounds = "rounds" , secondLast6 = "secondLast6" , secondFighterWeight = "secondFighterWeight" , secondRating = "secondRating" , secondRecord = "secondw-l-d" , sport = "sport" , stance = "stance" , record = "w-l-d" , sex = "sex" , tick = "tick" , venue = "venue" } export function getValueForHeadersArr ( headersArr , firstValue , secondValue ) { const hasRecord = headersArr . some ( item => item === firstValue ) ; if ( hasRecord ) { return secondValue ; } return firstValue ; } param tableEl param columnNumber param returnHTML export function getTableColumnData ( tableEl , columnNumber = 1 , returnHTML = false ) { const arr = [ ] ; tableEl . clone ( ) . find ( `tbody tr` ) . each ( function ( this , i , elem ) : void { const $ = cheerio . load ( elem ) ; const tdColumn = $ ( this ) . find ( `td:nth-child(${ columnNumber })` ) ; const data = returnHTML ? tdColumn . html ( ) : tdColumn . text ( ) ; if ( data ) { arr . push ( trimRemoveLineBreaks ( stripArrows ( data ) ) ) ; } } ) ; return arr ; } param tableEl param theadNumber export function getHeaderColumnText ( tableEl , theadNumber = 1 ) { const headersArr = [ ] ; tableEl . clone ( ) . find ( `thead:nth-child(${ theadNumber }) th` ) . each ( function ( this , i , elem ) : void { const $ = cheerio . load ( elem ) ; const headerText = trimRemoveLineBreaks ( stripArrows ( $ ( this ) . text ( ) ) ) ; const tbodyColumnEl = tableEl . clone ( ) . find ( `> tbody tr:nth-child(1) td:nth-child(${ i + 1 })` ) ; const rowDataText = trimRemoveLineBreaks ( tbodyColumnEl . text ( ) ) ; if ( headerText . length === 0 ) { if ( tbodyColumnEl . find ( ".starRating" ) . length ) { headersArr . push ( BoxrecCommonTableHeader . rating ) ; return ; } if ( tbodyColumnEl . find ( ".tick" ) . length ) { headersArr . push ( BoxrecCommonTableHeader . tick ) ; return ; } if ( tbodyColumnEl . find ( "a[href*='/event/']" ) . length ) { headersArr . push ( BoxrecCommonTableHeader . links ) ; return ; } if ( tbodyColumnEl . find ( ".boutResult" ) . length ) { headersArr . push ( BoxrecCommonTableHeader . outcome ) ; return ; } if ( tbodyColumnEl . find ( ".flag" ) . length ) { headersArr . push ( BoxrecCommonTableHeader . location ) ; return ; } if ( rowDataText . length > 0 && ! isNaN ( rowDataText as unknown as number ) || /[¼½¾]/ . test ( rowDataText ) ) { headersArr . push ( getValueForHeadersArr ( headersArr , BoxrecCommonTableHeader . firstFighterWeight , BoxrecCommonTableHeader . secondFighterWeight ) ) ; return ; } if ( $ ( `<div>${ tbodyColumnEl . html ( ) }</div>` ) . find ( ".personLink" ) . length === 1 ) { headersArr . push ( getValueForHeadersArr ( headersArr , BoxrecCommonTableHeader . fighter , BoxrecCommonTableHeader . opponent ) ) ; return ; } } if ( headerText . includes ( "lbs" ) || headerText . includes ( "kilos" ) ) { headersArr . push ( getValueForHeadersArr ( headersArr , BoxrecCommonTableHeader . firstFighterWeight , BoxrecCommonTableHeader . secondFighterWeight ) ) ; return ; } if ( headerText === "w-l-d" ) { headersArr . push ( getValueForHeadersArr ( headersArr , BoxrecCommonTableHeader . record , BoxrecCommonTableHeader . secondRecord ) ) ; return ; } if ( headerText === "last 6" ) { const hasFirstFighterLast6 = headersArr . some ( item => item === BoxrecCommonTableHeader . firstLast6 ) ; if ( hasFirstFighterLast6 ) { headersArr . push ( BoxrecCommonTableHeader . secondLast6 ) ; } else { headersArr . push ( BoxrecCommonTableHeader . firstLast6 ) ; } return ; } if ( headerText === "rating" && rowDataText . includes ( "➞" ) ) { const hasFirstRating = headersArr . some ( item => item === BoxrecCommonTableHeader . firstRating ) ; if ( hasFirstRating ) { headersArr . push ( BoxrecCommonTableHeader . secondRating ) ; } else { headersArr . push ( BoxrecCommonTableHeader . firstRating ) ; } return ; } if ( headerText === "" ) { const tableDataRows = getTableColumnData ( tableEl , i + 1 ) ; const uniqueVals = tableDataRows . filter ( ( elemItem , pos , arr ) => arr . indexOf ( elemItem ) === pos ) ; const outcomeKeys = Object . keys ( BoxingBoutOutcome ) ; const numberOfOccurrences = uniqueVals . map ( item => outcomeKeys . findIndex ( k => k === item ) ) ; if ( numberOfOccurrences . length ) { const totalOccurrences = numberOfOccurrences . reduce ( ( acc , curValue ) => acc + curValue ) ; if ( totalOccurrences > 0 ) { headersArr . push ( BoxrecCommonTableHeader . outcomeByWayOf ) ; return ; } } } headersArr . push ( headerText as BoxrecCommonTableHeader ) ; return ; } ) ; return headersArr ; } export const townRegionCountryRegex = /\?country=([A-Za-z]+)(?:(?:&|&amp;)region=([A-Za-z0-9]*))?(?:(?:&|&amp;)town=(\d+))?/ ; param { string } str returns { string } export const stripCommas = ( str ) : string => str . replace ( /,/g , "" ) ; param str export const stripArrows = ( str ) : string => str . replace ( /[↕↓↑]/g , "" ) ; export const whatTypeOfLink = ( href ) : "town" | "region" | "country" => { const matches = href . match ( /(\w+)\=(\w+)/g ) ; const locationObj = { country : null , region : null , town : null , } ; if ( matches ) { for ( const match of matches ) { const splitQuery = querystring . parse ( match ) ; const arr = Object . keys ( splitQuery ) . map ( x => [ x , splitQuery [ x ] ] ) [ 0 ] as any ; locationObj [ arr [ 0 ] ] = arr [ 1 ] ; } } if ( locationObj . country && locationObj . region && locationObj . town ) { return "town" ; } else if ( locationObj . country && locationObj . region ) { return "region" ; } return "country" ; } ; export const getLocationValue = ( href , type ) : string | number | null => { const matches = href . match ( /(\w+)=(\w+)/g ) ; if ( matches ) { for ( const match of matches ) { const splitQuery = querystring . parse ( match ) ; const keys = Object . keys ( splitQuery ) ; if ( keys [ 0 ] === type ) { return splitQuery [ keys [ 0 ] ] as any ; } } } return null ; } ; export const replaceWithWeight = ( str ) => trimRemoveLineBreaks ( str ) . replace ( /(\w+)\sTitle$/i , "$1weight Title" ) ; export const parseHeight = ( height ) : number [ ] | null => { if ( height ) { let regex = /^(\d)\′\s(\d+)(½)?\″\s+\/\s+(\d{3})cm$/ ; if ( height . includes ( "#x2032" ) ) { regex = /^(\d)\&\#x2032\;\s(\d{1,2})(\&\#xB[CDE]\;)?\&\#x2033\;\s\&\#xA0\;\s\/\s\&\#xA0\;\s(\d{3})cm$/ ; } const heightMatch = height . match ( regex ) ; if ( heightMatch ) { const [ , imperialFeet , imperialInches , fractionInches , metric ] = heightMatch ; let formattedImperialInches = parseInt ( imperialInches , 10 ) ; formattedImperialInches += convertFractionsToNumber ( fractionInches ) ; return [ parseInt ( imperialFeet , 10 ) , formattedImperialInches , parseInt ( metric , 10 ) , ] ; } } return null ; } ;