import _ from 'lodash' ; import { Emotes } from 'twitch-js' ; import { Word } from 'unistring' ; import { FfzEmote } from 'libs/Ffz' ; import Resources from 'libs/Resources' ; /**
 * Various emotes providers.
 */ export enum EmoteProviderPrefix { Bttv = 'bttv' , Ffz = 'ffz' , Twitch = 'twitch' } /**
 * Twitch RegExp emotes map.
 */ export const TwitchRegExpEmotesMap = { 'B-?\\)' : 'B)' , 'R-?\\)' : 'R)' , '[oO](_|\\.)[oO]' : 'O_o' , '\\&gt\\;\\(' : '>(' , '\\&lt\\;3' : '<3' , '\\:-?(o|O)' : ':O' , '\\:-?(p|P)' : ':P' , '\\:-?D' : ':D' , '\\:-?[\\\\/]' : ':/' , '\\:-?[z|Z|\\|]' : ':|' , '\\:-?\\(' : ':(' , '\\:-?\\)' : ':)' , '\\;-?(p|P)' : ';P' , '\\;-?\\)' : ';)' , } ; /**
 * Specific widths map for emotes known to be larger than the default width (28px).
 */ const EmotesWidthsMap = { '54fab45f633595ca4c713abc' : 36 , '54fab7d2633595ca4c713abf' : 42 , '54fbefeb01abde735115de5b' : 87 , '54fbf00a01abde735115de5c' : 46 , '5502874d135896936880fdd2' : 36 , '55189a5062e6bd0027aee082' : 60 , '5622aaef3286c42e57d8e4ab' : 32 , '566c9fc265dbbdab32ec053b' : 30 , '566c9fde65dbbdab32ec053e' : 30 , '566c9ff365dbbdab32ec0541' : 53 , '566ca00f65dbbdab32ec0544' : 56 , '566ca06065dbbdab32ec054e' : 38 , '566ca1a365dbbdab32ec055b' : 84 , '566ca38765dbbdab32ec0560' : 35 , '573d38b50ffbf6cc5cc38dc9' : 37 , 1900 : 33 , 28 : 39 , 30 : 29 , 36 : 36 , 52 : 32 , 65 : 40 , 69 : 41 , 86 : 36 , } ; /**
 * Characters triggering a next word look-up when parsing emotes.
 */ const nextWordLookUpTriggers = [ ';' , '<' , '>' , '(' , ':' , 'B' , 'D' , 'R' ] ; /**
 * EmotesProvider class.
 */ export default class EmotesProvider < ExternalEmote extends Emote > { param emotes return public static sanitizeTwitchEmotes ( emotes ) { return _ . map ( emotes , ( emote ) => { if ( EmotesProvider . isTwitchRegExpEmote ( emote . code ) ) { return { ... emote , code : TwitchRegExpEmotesMap [ emote . code ] } ; } return emote ; } ) ; } param prefix return public static isValidPrefix ( prefix ) { return _ . some ( EmoteProviderPrefix , ( value ) => value === prefix ) ; } param prefix return public static isTwitchPrefix ( prefix ) { return prefix === EmoteProviderPrefix . Twitch ; } param code return private static isTwitchRegExpEmote ( code ) { return code in TwitchRegExpEmotesMap ; } param id return private static isFixedWidthEmote ( id ) { return id in EmotesWidthsMap ; } private urlCompiledTemplate : Optional < _ . TemplateExecutor > ; class param prefix param emotes param urlTemplate param sizePrefix constructor ( public prefix , public emotes , urlTemplate ? , private sizePrefix ? ) { if ( ! _ . isNil ( urlTemplate ) ) { _ . templateSettings . interpolate = /{{([\s\S]+?)}}/g ; this . urlCompiledTemplate = _ . template ( urlTemplate ) ; } } return public isTwitch ( ) { return EmotesProvider . isTwitchPrefix ( this . prefix ) ; } param words return public getMessageEmotes ( words ) { return _ . reduce ( this . isTwitch ( ) ? [ ... this . emotes , ... _ . values ( Resources . manager ( ) . getEmoticonsMap ( ) ) ] : this . emotes , ( emotes , emote ) => { const wordsMatchingEmote = _ . filter ( words , ( word , index ) => { if ( _ . includes ( nextWordLookUpTriggers , word . text ) ) { return this . getPotentialNextWord ( words , index ) === emote . code ; } return word . text === emote . code ; } ) ; if ( wordsMatchingEmote . length === 0 ) { return emotes ; } const ranges = _ . map ( wordsMatchingEmote , ( word ) => [ word . index , word . index + emote . code . length - 1 ] . join ( '-' ) ) ; emotes [ `${ this . prefix }-${ emote . id }` ] = ranges ; return emotes ; } , { } as Emotes ) ; } param id param name return public getEmoteTag ( id , name ) { const { src , srcset } = this . getEmoteTagUrls ( id ) ; const minWidth = this . getSpecificWidth ( id ) ; return `<span class="emoteWrapper"${ minWidth }><img class="emote" data-tip="${ name }" data-id="${ id }" data-provider="${ this . prefix }" src="${ src }" srcset="${ srcset }" alt="${ name }" /></span>` ; } param id return public getEmoteTagUrls ( id ) { let url1x ; let url2x ; let url4x ; let srcset ; if ( this . urlCompiledTemplate ) { url1x = this . urlCompiledTemplate ( { id , image : this . getSizePath ( 1 ) } ) ; url2x = this . urlCompiledTemplate ( { id , image : this . getSizePath ( 2 ) } ) ; url4x = this . urlCompiledTemplate ( { id , image : this . getSizePath ( 3 ) } ) ; srcset = `${ url1x } 1x,${ url2x } 2x,${ url4x } 4x` ; } else if ( this . isFfz ( ) ) { const emote = this . getFfzEmote ( id ) ; if ( _ . isNil ( emote ) ) { throw new Error ( `Unknown Ffz emote (id: ${ id }).` ) ; } url1x = emote . urls [ 1 ] ; url2x = emote . urls [ 2 ] ; url4x = emote . urls [ 4 ] ; srcset = `${ url1x } 1x${ ! _ . isNil ( url2x ) ? `,${ url2x } 2x` : '' }${ ! _ . isNil ( url4x ) ? `,${ url4x } 4x` : '' }` ; } else { throw new Error ( 'Unable to generate emote tag URLs.' ) ; } return { '1x' : url1x , '2x' : url2x , '4x' : url4x , src : url1x , srcset , } ; } param id param size return public getEmoteUrlWithSize ( id , size ) { const urls = this . getEmoteTagUrls ( id ) ; return _ . get ( urls , size , urls [ '1x' ] ) ; } return public isFfz ( ) { return this . prefix === EmoteProviderPrefix . Ffz ; } param id return private getFfzEmote ( id ) { return _ . find ( this . emotes , ( emote ) => emote . id === id ) as Optional < FfzEmote > ; } param size return private getSizePath ( size ) { return `${ size . toString ( ) }${ this . sizePrefix }` ; } param words param index return private getPotentialNextWord ( words , index ) { return _ . get ( words [ index ] , 'text' ) . concat ( _ . get ( words [ index + 1 ] , 'text' , '' ) ) . concat ( _ . get ( words [ index + 2 ] , 'text' , '' ) ) . trim ( ) ; } param id return private getSpecificWidth ( id ) { let width ; if ( ! this . isFfz ( ) && EmotesProvider . isFixedWidthEmote ( id ) ) { width = EmotesWidthsMap [ id ] ; } else if ( this . isFfz ( ) ) { const emote = this . getFfzEmote ( id ) ; width = emote ?. width ; } return ! _ . isNil ( width ) ? `  style="min-width: ${ width }px;"` : '' ; } } /**
 * Base Twitch emote.
 */ export type Emote = { id : string ; code : string ; } ; /**
 * URLs used by an emote HTML tag.
 */ export type EmoteTagUrls = { '1x' : string ; '2x' ? : string ; '4x' ? : string ; src : string ; srcset : string ; } ;