import Constants from './constants' ; import Word from './word' ; import Wordlist from './wordlist' ; import Config from './config' ; export default class Filter { cfg : Config ; counter : number ; iWhitelist : string [ ] ; whitelist : string [ ] ; wordlistId : number ; wordlists : { [ name ] : Wordlist ; } ; constructor ( ) { this . counter = 0 ; this . iWhitelist = [ ] ; this . whitelist = [ ] ; this . wordlists = { } ; } buildWordlist ( wordlistId , rebuild = false ) { if ( wordlistId === false ) { wordlistId = this . wordlistId ; } if ( rebuild || ! this . wordlists [ wordlistId ] ) { this . wordlists [ wordlistId ] = new Wordlist ( this . cfg , wordlistId ) ; } return wordlistId ; } checkWhitelist ( match , string , matchStartIndex , word ) { const whitelistLength = this . whitelist . length ; const iWhitelistLength = this . iWhitelist . length ; if ( whitelistLength || iWhitelistLength ) { if ( whitelistLength && this . whitelist . includes ( match ) ) { return true ; } if ( iWhitelistLength && this . iWhitelist . includes ( match . toLowerCase ( ) ) ) { return true ; } if ( word . matchMethod === Constants . MATCH_METHODS . PARTIAL ) { const wordOptions = { matchMethod : Constants . MATCH_METHODS . WHOLE , repeat : Constants . FALSE , separators : Constants . FALSE , sub : '' } ; const wholeWordRegExp = new Word ( match , wordOptions , this . cfg ) . regExp ; let result ; while ( ( result = wholeWordRegExp . exec ( string ) ) !== null ) { const resultMatch = result . length == 4 ? result [ 2 ] : result [ 0 ] ; const resultIndex = result . length == 4 ? result . index + result [ 1 ] . length : result . index ; if ( resultIndex <= matchStartIndex && ( resultIndex + resultMatch . length ) >= ( matchStartIndex + match . length ) ) { if ( whitelistLength && this . whitelist . includes ( resultMatch ) ) { return true ; } if ( iWhitelistLength && this . iWhitelist . includes ( resultMatch . toLowerCase ( ) ) ) { return true ; } } } } } return false ; } foundMatch ( word ? , statsType ? ) { this . counter ++ ; } init ( wordlistId = false ) { this . iWhitelist = this . cfg . iWordWhitelist ; this . whitelist = this . cfg . wordWhitelist ; if ( this . wordlistId === undefined ) { this . wordlistId = this . cfg . wordlistId == null ? Constants . ALL_WORDS_WORDLIST_ID : this . cfg . wordlistId ; } this . buildWordlist ( wordlistId ) ; } matchData ( wordlist , index , match , args ) { const word = wordlist . find ( index ) ; const string = args . pop ( ) ; const matchStartIndex = args . pop ( ) ; const captureGroups = args ; const internalCaptureGroups = ( captureGroups . length > 0 && word . matchMethod !== Constants . MATCH_METHODS . REGEX ) ; if ( internalCaptureGroups ) { match = captureGroups [ 1 ] ; } return { word : word , string : string , match : match , matchStartIndex : matchStartIndex , captureGroups : captureGroups , internalCaptureGroups : internalCaptureGroups } ; } rebuildWordlists ( ) { Object . keys ( this . wordlists ) . forEach ( ( key ) => { this . buildWordlist ( parseInt ( key ) , true ) ; } ) ; } replaceText ( str , wordlistId = false , statsType = Constants . STATS_TYPE_TEXT ) { wordlistId = this . buildWordlist ( wordlistId ) ; const wordlist = this . wordlists [ wordlistId ] ; switch ( this . cfg . filterMethod ) { case Constants . FILTER_METHODS . OFF : case Constants . FILTER_METHODS . CENSOR : wordlist . regExps . forEach ( ( regExp , index ) => { str = str . replace ( regExp , ( originalMatch , ... args ) : string => { const { word , string , match , matchStartIndex , captureGroups , internalCaptureGroups } = this . matchData ( wordlist , index , originalMatch , args ) ; if ( this . checkWhitelist ( match , string , matchStartIndex , word ) ) { return match ; } if ( statsType ) { this . foundMatch ( word , statsType ) ; } let censoredString = '' ; const censorLength = this . cfg . censorFixedLength > 0 ? this . cfg . censorFixedLength : match . length ; if ( this . cfg . preserveFirst && this . cfg . preserveLast ) { censoredString = match [ 0 ] + this . cfg . censorCharacter . repeat ( censorLength - 2 ) + match . slice ( - 1 ) ; } else if ( this . cfg . preserveFirst ) { censoredString = match [ 0 ] + this . cfg . censorCharacter . repeat ( censorLength - 1 ) ; } else if ( this . cfg . preserveLast ) { censoredString = this . cfg . censorCharacter . repeat ( censorLength - 1 ) + match . slice ( - 1 ) ; } else { censoredString = this . cfg . censorCharacter . repeat ( censorLength ) ; } if ( internalCaptureGroups ) { censoredString = captureGroups [ 0 ] + censoredString + captureGroups [ 2 ] ; } return censoredString ; } ) ; } ) ; break ; case Constants . FILTER_METHODS . SUBSTITUTE : wordlist . regExps . forEach ( ( regExp , index ) => { str = str . replace ( regExp , ( originalMatch , ... args ) : string => { const { word , string , match , matchStartIndex , captureGroups , internalCaptureGroups } = this . matchData ( wordlist , index , originalMatch , args ) ; if ( this . checkWhitelist ( match , string , matchStartIndex , word ) ) { return match ; } if ( statsType ) { this . foundMatch ( word , statsType ) ; } let sub = word . sub || this . cfg . defaultSubstitution ; if ( ! word . case && this . cfg . preserveCase ) { if ( Word . allUpperCase ( match ) ) { sub = sub . toUpperCase ( ) ; } else if ( Word . eachWordCapitalized ( match ) ) { sub = Word . capitalizeEachWord ( sub ) ; } else if ( Word . firstCapitalized ( match ) ) { sub = Word . capitalizeFirst ( sub ) ; } } if ( this . cfg . substitutionMark ) { sub = '[' + sub + ']' ; } if ( internalCaptureGroups ) { sub = captureGroups [ 0 ] + sub + captureGroups [ 2 ] ; } return sub ; } ) ; } ) ; break ; case Constants . FILTER_METHODS . REMOVE : wordlist . regExps . forEach ( ( regExp , index ) => { str = str . replace ( regExp , ( originalMatch , ... args ) : string => { const { word , string , match , matchStartIndex , captureGroups , internalCaptureGroups } = this . matchData ( wordlist , index , originalMatch , args ) ; if ( this . checkWhitelist ( match . trim ( ) , string , matchStartIndex , word ) ) { return match ; } if ( statsType ) { this . foundMatch ( word , statsType ) ; } if ( internalCaptureGroups ) { if ( Word . whitespaceRegExp . test ( captureGroups [ 0 ] ) && Word . whitespaceRegExp . test ( captureGroups [ 2 ] ) ) { return captureGroups [ 0 ] ; } else if ( Word . nonWordRegExp . test ( captureGroups [ 0 ] ) || Word . nonWordRegExp . test ( captureGroups [ 2 ] ) ) { return ( captureGroups [ 0 ] + captureGroups [ 2 ] ) . trim ( ) ; } else { return '' ; } } else { if ( Word . whitespaceRegExp . test ( match [ 0 ] ) && Word . whitespaceRegExp . test ( match [ match . length - 1 ] ) ) { return match [ 0 ] ; } else { return '' ; } } } ) ; } ) ; break ; } return str ; } replaceTextResult ( str , wordlistId = false , statsType = Constants . STATS_TYPE_TEXT ) { const result = { original : str , filtered : this . replaceText ( str , wordlistId , statsType ) , modified : false } ; result . modified = ( result . filtered != str ) ; return result ; } }