import { Injectable } from '@angular/core' ; import { CompressedEmojiData , EmojiData , EmojiVariation , Emoji } from '../interface/emoji.interfaces' ; import { emojis } from '../constant/emoji/emojis' ; const COLONS_REGEX = /^(?:\:([^\:]+)\:)(?:\:skin-tone-(\d)\:)?$/ ; const SKINS = [ '1F3FA' , '1F3FB' , '1F3FC' , '1F3FD' , '1F3FE' , '1F3FF' ] ; export const DEFAULT_BACKGROUND_FN = ( set , sheetSize ) => `https://unpkg.com/emoji-datasource-${ set }@4.0.4/img/${ set }/sheets-256/${ sheetSize }.png` ; @ Injectable ( { providedIn : 'root' } ) export class EmojiService { names : { [ key ] : EmojiData ; } = { } ; emojis : EmojiData [ ] = [ ] ; constructor ( ) { this . uncompress ( emojis ) ; } private uncompress ( list ) { this . emojis = list . map ( emoji => { const data = { ... emoji } ; if ( ! data . shortNames ) { data . shortNames = [ ] ; } data . shortNames . unshift ( data . shortName ) ; data . id = data . shortName ; data . native = this . unifiedToNative ( data . unified ) ; if ( ! data . skinVariations ) { data . skinVariations = [ ] ; } if ( ! data . keywords ) { data . keywords = [ ] ; } if ( ! data . emoticons ) { data . emoticons = [ ] ; } if ( ! data . hidden ) { data . hidden = [ ] ; } if ( ! data . text ) { data . text = '' ; } if ( data . obsoletes ) { const f = list . find ( x => x . unified === data . obsoletes ) ; if ( f ) { if ( f . keywords ) { data . keywords = [ ... data . keywords , ... f . keywords , f . shortName ] ; } else { data . keywords = [ ... data . keywords , f . shortName ] ; } } } this . names [ data . unified ] = data ; for ( const n of data . shortNames ) { this . names [ n ] = data ; } return data ; } ) ; } private getData ( emoji , skin ? , set ? ) { let emojiData ; if ( typeof emoji === 'string' ) { const matches = emoji . match ( COLONS_REGEX ) ; if ( matches ) { emoji = matches [ 1 ] ; if ( matches [ 2 ] ) { skin = parseInt ( matches [ 2 ] , 10 ) as Emoji [ 'skin' ] ; } } if ( this . names . hasOwnProperty ( emoji ) ) { emojiData = this . names [ emoji ] ; } else { return null ; } } else if ( emoji . id ) { emojiData = this . names [ emoji . id ] ; } else if ( emoji . unified ) { emojiData = this . names [ emoji . unified . toUpperCase ( ) ] ; } if ( ! emojiData ) { emojiData = emoji ; emojiData . custom = true ; } const hasSkinVariations = emojiData . skinVariations && emojiData . skinVariations . length ; if ( hasSkinVariations && skin && skin > 1 && set ) { emojiData = { ... emojiData } ; const skinKey = SKINS [ skin - 1 ] ; const variationData = emojiData . skinVariations . find ( ( n ) => n . unified . includes ( skinKey ) ) ; if ( ! variationData . hidden || ! variationData . hidden . includes ( set ) ) { emojiData . skinTone = skin ; emojiData = { ... emojiData , ... variationData } ; } emojiData . native = this . unifiedToNative ( emojiData . unified ) ; } emojiData . set = set || '' ; return emojiData as EmojiData ; } private unifiedToNative ( unified ) { const codePoints = unified . split ( '-' ) . map ( u => parseInt ( `0x${ u }` , 16 ) ) ; return String . fromCodePoint ( ... codePoints ) ; } emojiSpriteStyles ( sheet , set = 'apple' , size = 24 , sheetSize = 64 , backgroundImageFn = DEFAULT_BACKGROUND_FN , sheetColumns = 52 ) { return { width : `${ size }px` , height : `${ size }px` , display : 'inline-block' , 'background-image' : `url(${ backgroundImageFn ( set , sheetSize ) })` , 'background-size' : `${ 100 * sheetColumns }%` , 'background-position' : this . getSpritePosition ( sheet , sheetColumns ) , } ; } private getSpritePosition ( sheet , sheetColumns ) { const [ sheet_x , sheet_y ] = sheet ; const multiply = 100 / ( sheetColumns - 1 ) ; return `${ multiply * sheet_x }% ${ multiply * sheet_y }%` ; } private sanitize ( emoji ) { if ( emoji === null ) { return null ; } const id = emoji . id || emoji . shortNames [ 0 ] ; let colons = `:${ id }:` ; if ( emoji . skinTone ) { colons += `:skin-tone-${ emoji . skinTone }:` ; } emoji . colons = colons ; return { ... emoji } ; } private getSanitizedData ( emoji , skin ? , set ? ) { return this . sanitize ( this . getData ( emoji , skin , set ) ) ; } param emoji returns getStyle ( emoji ) { const data = this . getData ( emoji ) ; if ( ! data ) { return '' ; } else { const spriteStyle = this . emojiSpriteStyles ( data . sheet , 'apple' , 18 , 64 , DEFAULT_BACKGROUND_FN , 52 ) ; return Object . entries ( spriteStyle ) . reduce ( ( pre , [ key , value ] ) => `${ pre }; ${ key }: ${ value }; ` , 'margin: 0 5px 0 0' ) ; } } }