enum ContextualForm { Initial , Medial , Final } enum CombinedForm { Isolated , Connected } /**
 * Converter between arabic isolated forms (in Unicode Block 'Arabic') and their contextual forms
 * (in Unicode Block 'Arabic Presentation Forms-B').
 */ export class ContextualArabicConverter { private static m_instance : ContextualArabicConverter ; static get instance ( ) : ContextualArabicConverter { if ( this . m_instance === undefined ) { this . m_instance = new ContextualArabicConverter ( ) ; } return this . m_instance ; } private readonly m_singleCharactersMap : Map < number , ReadonlyArray < number | undefined > > = new Map ( ) ; private readonly m_combinedCharactersMap : Map < number , Map < number , ReadonlyArray < number | undefined > > > = new Map ( ) ; private readonly m_neutralCharacters : number [ ] ; private constructor ( ) { this . m_singleCharactersMap . set ( 0x0621 , [ undefined , undefined , undefined ] ) ; this . m_singleCharactersMap . set ( 0x0622 , [ undefined , undefined , 0xfe82 ] ) ; this . m_singleCharactersMap . set ( 0x0623 , [ undefined , undefined , 0xfe84 ] ) ; this . m_singleCharactersMap . set ( 0x0624 , [ undefined , undefined , 0xfe86 ] ) ; this . m_singleCharactersMap . set ( 0x0625 , [ undefined , undefined , 0xfe88 ] ) ; this . m_singleCharactersMap . set ( 0x0626 , [ 0xfe8b , 0xfe8c , 0xfe8a ] ) ; this . m_singleCharactersMap . set ( 0x0627 , [ undefined , undefined , 0xfe8e ] ) ; this . m_singleCharactersMap . set ( 0x0628 , [ 0xfe91 , 0xfe92 , 0xfe90 ] ) ; this . m_singleCharactersMap . set ( 0x0629 , [ undefined , undefined , 0xfe94 ] ) ; this . m_singleCharactersMap . set ( 0x062a , [ 0xfe97 , 0xfe98 , 0xfe96 ] ) ; this . m_singleCharactersMap . set ( 0x062b , [ 0xfe9b , 0xfe9c , 0xfe9a ] ) ; this . m_singleCharactersMap . set ( 0x062c , [ 0xfe9f , 0xfea0 , 0xfe9e ] ) ; this . m_singleCharactersMap . set ( 0x062d , [ 0xfea3 , 0xfea4 , 0xfea2 ] ) ; this . m_singleCharactersMap . set ( 0x062e , [ 0xfea7 , 0xfea8 , 0xfea6 ] ) ; this . m_singleCharactersMap . set ( 0x062f , [ undefined , undefined , 0xfeaa ] ) ; this . m_singleCharactersMap . set ( 0x0630 , [ undefined , undefined , 0xfeac ] ) ; this . m_singleCharactersMap . set ( 0x0631 , [ undefined , undefined , 0xfeae ] ) ; this . m_singleCharactersMap . set ( 0x0632 , [ undefined , undefined , 0xfeb0 ] ) ; this . m_singleCharactersMap . set ( 0x0633 , [ 0xfeb3 , 0xfeb4 , 0xfeb2 ] ) ; this . m_singleCharactersMap . set ( 0x0634 , [ 0xfeb7 , 0xfeb8 , 0xfeb6 ] ) ; this . m_singleCharactersMap . set ( 0x0635 , [ 0xfebb , 0xfebc , 0xfeba ] ) ; this . m_singleCharactersMap . set ( 0x0636 , [ 0xfebf , 0xfec0 , 0xfebe ] ) ; this . m_singleCharactersMap . set ( 0x0637 , [ 0xfec3 , 0xfec4 , 0xfec2 ] ) ; this . m_singleCharactersMap . set ( 0x0638 , [ 0xfec7 , 0xfec8 , 0xfec6 ] ) ; this . m_singleCharactersMap . set ( 0x0639 , [ 0xfecb , 0xfecc , 0xfeca ] ) ; this . m_singleCharactersMap . set ( 0x063a , [ 0xfecf , 0xfed0 , 0xfece ] ) ; this . m_singleCharactersMap . set ( 0x0640 , [ 0x0640 , 0x0640 , 0x0640 ] ) ; this . m_singleCharactersMap . set ( 0x0641 , [ 0xfed3 , 0xfed4 , 0xfed2 ] ) ; this . m_singleCharactersMap . set ( 0x0642 , [ 0xfed7 , 0xfed8 , 0xfed6 ] ) ; this . m_singleCharactersMap . set ( 0x0643 , [ 0xfedb , 0xfedc , 0xfeda ] ) ; this . m_singleCharactersMap . set ( 0x0644 , [ 0xfedf , 0xfee0 , 0xfede ] ) ; this . m_singleCharactersMap . set ( 0x0645 , [ 0xfee3 , 0xfee4 , 0xfee2 ] ) ; this . m_singleCharactersMap . set ( 0x0646 , [ 0xfee7 , 0xfee8 , 0xfee6 ] ) ; this . m_singleCharactersMap . set ( 0x0647 , [ 0xfeeb , 0xfeec , 0xfeea ] ) ; this . m_singleCharactersMap . set ( 0x0648 , [ undefined , undefined , 0xfeee ] ) ; this . m_singleCharactersMap . set ( 0x0649 , [ undefined , undefined , 0xfef0 ] ) ; this . m_singleCharactersMap . set ( 0x064a , [ 0xfef3 , 0xfef4 , 0xfef2 ] ) ; this . m_singleCharactersMap . set ( 0x067e , [ 0xfb58 , 0xfb59 , 0xfb57 ] ) ; this . m_singleCharactersMap . set ( 0x06cc , [ 0xfbfe , 0xfbff , 0xfbfd ] ) ; this . m_singleCharactersMap . set ( 0x0686 , [ 0xfb7c , 0xfb7d , 0xfb7b ] ) ; this . m_singleCharactersMap . set ( 0x06a9 , [ 0xfb90 , 0xfb91 , 0xfb8f ] ) ; this . m_singleCharactersMap . set ( 0x06af , [ 0xfb94 , 0xfb95 , 0xfb93 ] ) ; this . m_singleCharactersMap . set ( 0x0698 , [ undefined , undefined , 0xfb8b ] ) ; this . m_combinedCharactersMap . set ( 0x0644 , new Map ( ) ) ; this . m_combinedCharactersMap . get ( 0x0644 ) ! . set ( 0x0622 , [ 0xfef5 , 0xfef6 ] ) ; this . m_combinedCharactersMap . get ( 0x0644 ) ! . set ( 0x0623 , [ 0xfef7 , 0xfef8 ] ) ; this . m_combinedCharactersMap . get ( 0x0644 ) ! . set ( 0x0625 , [ 0xfef9 , 0xfefa ] ) ; this . m_combinedCharactersMap . get ( 0x0644 ) ! . set ( 0x0627 , [ 0xfefb , 0xfefc ] ) ; this . m_neutralCharacters = [ 0x0610 , 0x0612 , 0x0613 , 0x0614 , 0x0615 , 0x064b , 0x064c , 0x064d , 0x064e , 0x064f , 0x0650 , 0x0651 , 0x0652 , 0x0653 , 0x0654 , 0x0655 , 0x0656 , 0x0657 , 0x0658 , 0x0670 , 0x06d6 , 0x06d7 , 0x06d8 , 0x06d9 , 0x06da , 0x06db , 0x06dc , 0x06df , 0x06e0 , 0x06e1 , 0x06e2 , 0x06e3 , 0x06e4 , 0x06e7 , 0x06e8 , 0x06ea , 0x06eb , 0x06ec , 0x06ed ] ; } param input convert ( input ) { let output = "" ; for ( let i = 0 ; i < input . length ; ++ i ) { const currentCodePoint = input . charCodeAt ( i ) ; if ( this . isArabicCharacter ( currentCodePoint ) ) { let prevIndex = i - 1 ; for ( ; prevIndex >= 0 ; -- prevIndex ) { if ( ! this . isNeutral ( input . charCodeAt ( prevIndex ) ) ) { break ; } } let prevCodePoint = prevIndex >= 0 ? input . charCodeAt ( prevIndex ) : undefined ; if ( prevCodePoint !== undefined ) { const prevMap = this . getCharacterMap ( prevCodePoint ) ; if ( prevMap === undefined || ( prevMap [ ContextualForm . Initial ] === undefined && prevMap [ ContextualForm . Medial ] === undefined ) ) { prevCodePoint = undefined ; } } let nextIndex = i + 1 ; for ( ; nextIndex < input . length ; ++ nextIndex ) { if ( ! this . isNeutral ( input . charCodeAt ( nextIndex ) ) ) { break ; } } let nextCodePoint = nextIndex < input . length ? input . charCodeAt ( nextIndex ) : undefined ; if ( nextCodePoint !== undefined ) { const nextMap = this . getCharacterMap ( nextCodePoint ) ; if ( nextMap === undefined || ( nextMap [ ContextualForm . Medial ] === undefined && nextMap [ ContextualForm . Final ] === undefined ) ) { nextCodePoint = undefined ; } } if ( currentCodePoint === 0x0644 && nextCodePoint !== undefined && ( nextCodePoint === 0x0622 || nextCodePoint === 0x0623 || nextCodePoint === 0x0625 || nextCodePoint === 0x0627 ) ) { const combinedMap = this . getCombinedCharacterMap ( currentCodePoint , nextCodePoint ) ! ; if ( prevCodePoint !== undefined ) { output += String . fromCharCode ( combinedMap [ CombinedForm . Connected ] ! ) ; } else { output += String . fromCharCode ( combinedMap [ CombinedForm . Isolated ] ! ) ; } ++ i ; continue ; } const map = this . getCharacterMap ( currentCodePoint ) ! ; if ( prevCodePoint !== undefined && nextCodePoint !== undefined && map [ ContextualForm . Medial ] !== undefined ) { output += String . fromCharCode ( map [ ContextualForm . Medial ] ! ) ; } else if ( prevCodePoint !== undefined && map [ ContextualForm . Final ] !== undefined ) { output += String . fromCharCode ( map [ ContextualForm . Final ] ! ) ; } else if ( nextCodePoint !== undefined && map [ ContextualForm . Initial ] !== undefined ) { output += String . fromCharCode ( map [ ContextualForm . Initial ] ! ) ; } else { output += String . fromCharCode ( currentCodePoint ) ; } } else { output += String . fromCharCode ( currentCodePoint ) ; } } return output ; } private isArabicCharacter ( codePoint ) { return this . m_singleCharactersMap . has ( codePoint ) ; } private getCharacterMap ( codePoint ) { return this . m_singleCharactersMap . get ( codePoint ) ; } private getCombinedCharacterMap ( codePoint , nextCodePoint ) { const map = this . m_combinedCharactersMap . get ( codePoint ) ; if ( map !== undefined ) { return map . get ( nextCodePoint ) ; } return undefined ; } private isNeutral ( codePoint ) { for ( const character of this . m_neutralCharacters ) { if ( character === codePoint ) { return true ; } } return false ; } }