import { Modifier , Priorities , EType , IHTMLInjection , EHTMLInjectionType , Logger , EApplyTo , } from 'chipmunk.client.toolkit' ; import { EParent } from '../../standalone/service.output.redirections' ; export class ModifierProcessor { private _modifiers : Modifier [ ] ; private _injections : IHTMLInjection [ ] = [ ] ; private _logger : Logger = new Logger ( 'ModifierProcessor' ) ; private _hasOwnStyles : boolean = false ; constructor ( modifiers , hasOwnStyles ) { this . _modifiers = modifiers ; this . _hasOwnStyles = hasOwnStyles ; } public parse ( row , parent ) { const cleanup = ( str ) : string => { this . _modifiers . forEach ( ( modifier ) => { const finalized = modifier . finalize ( str ) ; const safe = this . _serialize ( finalized ) ; if ( safe . length !== finalized . length ) { this . _logger . warn ( `Modifier "${ modifier . getName ( ) }" tries to inject HTML` ) ; } str = safe ; } ) ; return str ; } ; row = this . _serialize ( row ) ; this . _injections = [ ] ; const modifiers = this . _getApplicableModifiers ( parent ) ; Priorities . forEach ( ( type , index ) => { const subordinateTypes = index !== Priorities . length - 1 ? Priorities . slice ( index + 1 , Priorities . length ) : [ ] ; const masters = modifiers . filter ( ( m ) => m . type ( ) === type ) ; const subordinates = modifiers . filter ( ( m ) => subordinateTypes . indexOf ( m . type ( ) ) !== - 1 ) ; if ( masters . length === 0 ) { return ; } else { masters . sort ( ( a , b ) => ( a . getGroupPriority ( ) > b . getGroupPriority ( ) ? 1 : - 1 ) ) ; masters . forEach ( ( master , n ) => { for ( let i = n + 1 ; i <= masters . length - 1 ; i += 1 ) { masters [ i ] . obey ( master . getRanges ( ) ) ; } } ) ; } if ( subordinates . length === 0 ) { return ; } else { subordinates . sort ( ( a , b ) => ( a . getGroupPriority ( ) > b . getGroupPriority ( ) ? 1 : - 1 ) ) ; masters . forEach ( ( master ) => { subordinates . forEach ( ( subordinate ) => { subordinate . obey ( master . getRanges ( ) ) ; } ) ; } ) ; } } ) ; modifiers . forEach ( ( modifier ) => { let ignore = false ; modifier . getInjections ( ) . forEach ( ( inj ) => { const err = this . _getInjectionError ( inj . injection ) ; if ( err instanceof Error ) { this . _logger . warn ( `Injection of modifier "${ modifier . getName ( ) }" has been rejected due error: ${ err . message }` ) ; ignore = true ; } } ) ; if ( ! ignore ) { this . _injections . push ( ... modifier . getInjections ( ) ) ; } else { this . _logger . warn ( `All injections of modifier "${ modifier . getName ( ) }" will be ignored` ) ; } } ) ; this . _injections . sort ( ( a , b ) => { return a . offset < b . offset ? 1 : - 1 ; } ) ; let injections = [ ] ; this . _injections . forEach ( ( inj ) => { if ( injections . find ( ( a ) => a . offset === inj . offset ) !== undefined ) { return ; } const same = this . _injections . filter ( ( a ) => a . offset === inj . offset ) ; if ( same . length === 1 ) { injections . push ( inj ) ; } else { same . sort ( ( a , b ) => { return a . type === EHTMLInjectionType . close ? 1 : - 1 ; } ) ; injections = injections . concat ( same ) ; } } ) ; this . _injections = injections ; this . _injections . forEach ( ( inj ) => { row = row . substring ( 0 , inj . offset ) + inj . injection + row . substring ( inj . offset , row . length ) ; } ) ; if ( row . search ( /[\>\<]/g ) !== - 1 ) { row = row . replace ( /(^.*?\<)|(\>.*?\<)|(\>.*?$)/gi , ( match , ... args ) => { let clean = match . replace ( /[\>\<]/gi , '' ) ; if ( clean === '' ) { return match ; } clean = cleanup ( clean ) ; return `${ match [ 0 ] === '>' ? '>' : '' }${ clean }${ match [ match . length - 1 ] === '<' ? '<' : '' }` ; } ) ; } else { row = cleanup ( row ) ; } return row ; } public wasChanged ( ) { return this . _injections . length > 0 ; } private _getApplicableModifiers ( target ) { const applyTo = ( ( ) => { if ( target === EParent . output ) { return EApplyTo . output ; } if ( target === EParent . search ) { return EApplyTo . search ; } return EApplyTo . all ; } ) ( ) ; return this . _modifiers . filter ( ( modifier ) => { if ( modifier . applyTo ( ) !== EApplyTo . all && modifier . applyTo ( ) !== applyTo ) { return false ; } if ( this . _hasOwnStyles ) { return [ EType . above , EType . match ] . indexOf ( modifier . type ( ) ) !== - 1 ; } else { return true ; } } ) ; } private _serialize ( str ) { return str . replace ( /</gi , '&lt;' ) . replace ( />/gi , '&gt;' ) ; } private _getInjectionError ( injection ) { const tag = /\<\/?(span|b|i).*?\>/gi ; const match = injection . match ( tag ) ; if ( match === null ) { return new Error ( `Expecting "injection" has to be HTML tag (supported tags: <span>, <b> and <i>.)` ) ; } if ( match . length > 1 ) { return new Error ( `"injection" should return only one tag. Multiple tags are prohibited` ) ; } if ( injection . replace ( tag , '' ) . length !== 0 ) { return new Error ( `"injection" should return only tag without any content outside of tag.` ) ; } return undefined ; } }