import { spellInfo } from './spellToWord' ; import { _warn , isCnChar , has } from './util' ; import defultDict from './dict/spell-default.json' ; import { AllArgs , ICnChar , TypeProp , ToneType , SpellArg , StrokeArg , TypeValueObject } from 'cnchar-types/main/index' ; import { Json , ITransformReturn } from 'cnchar-types/main/common' ; import { TSpellArg , IDealUpLowFirst , IRemoveTone , IFunc , ICheckArgs , ITransformTone } from 'cnchar-types/main/tool' ; const defDict = defultDict as Json < string > ; export const tones = 'āáǎàōóǒòēéěèīíǐìūúǔùǖǘǚǜ*ńňǹ' ; const noTones = 'aoeiuün' ; export const arg = { array : 'array' , low : 'low' , up : 'up' , first : 'first' , poly : 'poly' , tone : 'tone' , simple : 'simple' , trad : 'trad' , } ; let _cnchar ; export function initCnchar ( cnchar ) { _cnchar = cnchar ; } const NOT_CNCHAR = 'NOT_CNCHAR' ; export function spell ( dict , originArgs ) { const strs = originArgs [ 0 ] . split ( '' ) ; const args = ( originArgs . splice ( 1 ) ) as Array < SpellArg > ; checkArgs ( 'spell' , args ) ; const poly = has ( args , arg . poly ) ; const tone = has ( args , arg . tone ) ; const res = [ ] ; for ( const sp in dict ) { const ds = dict [ sp ] ; const pos = parseInt ( ds [ 0 ] ) ; for ( let i = 0 ; i < strs . length ; i ++ ) { const ch = strs [ i ] ; if ( isCnChar ( ch ) ) { let index = ds . indexOf ( ch ) ; if ( index !== - 1 ) { const ssp = getSpell ( sp , ds , index , poly , tone , pos ) ; if ( ssp . poly ) { if ( ! res [ i ] ) { res [ i ] = [ ] ; } res [ i ] . push ( ssp . res ) ; let dsNew = ds ; const n = ( dsNew . match ( new RegExp ( ch , 'g' ) ) as Array < string > ) . length ; for ( let k = 1 ; k < n ; k ++ ) { dsNew = dsNew . substr ( index + 2 ) ; index = dsNew . indexOf ( ch ) ; res [ i ] . push ( getSpell ( sp , dsNew , index , poly , tone , pos ) . res ) ; } } else { if ( ssp . isPolyWord ) { if ( defDict [ ch ] ) { ssp . res = removeTone ( defDict [ ch ] , tone ) . spell ; } } res [ i ] = [ ssp . res ] ; strs [ i ] = '' ; } } } else if ( ch !== '' ) { res [ i ] = [ NOT_CNCHAR , ch ] ; } } } dealUpLowFirst ( res , args ) ; const result = [ ] ; for ( let i = 0 ; i < strs . length ; i ++ ) { const item = res [ i ] ; if ( typeof item === 'undefined' ) { result [ i ] = strs [ i ] ; } else if ( item . length > 1 ) { if ( item [ 0 ] === NOT_CNCHAR ) { result [ i ] = item [ 1 ] ; } else { result [ i ] = `(${ res [ i ] . join ( '|' ) })` ; } } else { result [ i ] = item [ 0 ] ; } } if ( ! has ( args , arg . array ) ) { return result . join ( '' ) ; } return result ; } export const dealUpLowFirst = ( res , args ) : void => { if ( _cnchar . _ . poly ) { dealResCase ( res , low ) ; } if ( has ( args , arg . first ) ) { dealResCase ( res , first ) ; } if ( has ( args , arg . up ) ) { dealResCase ( res , up ) ; } else if ( ! has ( args , arg . low ) ) { dealResCase ( res , upFirst ) ; } } ; function dealResCase ( res , func ) { res . forEach ( ( item , index ) => { if ( typeof item !== 'string' ) { if ( item [ 0 ] !== NOT_CNCHAR ) { item . forEach ( ( s , j ) => { item [ j ] = func ( s ) ; } ) ; } } else { res [ index ] = func ( item ) ; } } ) ; } function first ( s ) { return s [ 0 ] ; } function up ( s ) { return s . toUpperCase ( ) ; } function upFirst ( s ) { return up ( s [ 0 ] ) + s . substr ( 1 ) ; } function low ( s ) { return s . toLowerCase ( ) ; } function getSpell ( spell , str , index , isPoly , isTone , pos ) { let tone = parseInt ( str [ index + 1 ] ) ; const res = { res : spell , poly : false , isPolyWord : ( tone >= 5 ) } ; if ( ! isPoly && ! isTone ) { return res ; } if ( res . isPolyWord ) { tone -= 5 ; if ( isPoly ) { res . poly = true ; } } if ( isTone ) { res . res = setTone ( spell , pos , tone as ToneType ) ; } return res ; } export const removeTone = ( spell , tone ) : { spell : string ; tone ? : ToneType ; index ? : number ; } => { if ( tone ) { return { spell } ; } for ( let i = 0 ; i < spell . length ; i ++ ) { const index = tones . indexOf ( spell [ i ] ) ; if ( index !== - 1 ) { return { spell : spell . substr ( 0 , i ) + noTones [ Math . floor ( index / 4 ) ] + spell . substr ( i + 1 ) , tone : ( ( index % 4 ) + 1 ) as ToneType , index : i + 1 } ; } } return { spell , tone : 0 , index : - 1 } ; } ; function setTone ( spell , index , tone ) { if ( tone === 0 ) { return spell ; } const p = spell [ index ] ; const toneP = tones [ noTones . indexOf ( p ) * 4 + ( tone - 1 ) ] ; if ( p !== toneP ) { return spell . replace ( p , toneP ) ; } return spell ; } export function stroke ( dict , originArgs ) { const strs = originArgs [ 0 ] . split ( '' ) ; const strokes = [ ] ; const args = originArgs . splice ( 1 ) as Array < StrokeArg > ; checkArgs ( 'stroke' , args ) ; for ( const i in dict ) { for ( let j = 0 ; j < strs . length ; j ++ ) { if ( strs [ j ] ) { if ( dict [ i ] . indexOf ( strs [ j ] as string ) !== - 1 ) { strs [ j ] = '' ; strokes [ j ] = parseInt ( i ) ; } } } } strs . forEach ( ( c , i ) : void => { if ( c ) { strokes [ i ] = 0 ; } } ) ; if ( ! has ( args , arg . array as StrokeArg ) ) { return sumStroke ( strokes ) ; } return strokes ; } export const sumStroke = ( strs ) : number => { let sum = 0 ; strs . forEach ( function ( c ) { sum += c ; } ) ; return sum ; } ; let _hasCheck = false ; export const checkArgs = ( type , args , jumpNext ? ) : void => { if ( ! _cnchar . check ) { return ; } if ( _hasCheck ) { _hasCheck = false ; return ; } if ( jumpNext ) { _hasCheck = true ; } const useless = [ ] ; for ( let i = args . length - 1 ; i >= 0 ; i -- ) { const arg = args [ i ] ; if ( ! ( _cnchar . type [ type ] as TypeValueObject ) [ arg ] ) { useless . push ( arg ) ; args . splice ( i , 1 ) ; } } const ignore = [ ] ; const redunt = [ ] ; const check = ( name , arr = ignore ) : void => { if ( name instanceof Array ) { name . forEach ( ( item ) => { check ( item , arr ) ; } ) ; return ; } if ( has ( args , name ) ) { arr . push ( name ) ; } } ; if ( _cnchar . plugins . indexOf ( 'trad' ) === - 1 ) { if ( has ( args , 'simple' ) ) ignore . push ( 'simple' ) ; if ( has ( args , 'trad' ) ) ignore . push ( 'trad' ) ; } if ( type === 'spell' ) { if ( has ( args , 'up' ) && has ( args , 'low' ) ) { ignore . push ( 'low' ) ; } } else if ( type === 'stroke' ) { if ( has ( args , 'order' ) ) { check ( 'array' , redunt ) ; if ( has ( args , 'letter' ) ) { check ( [ 'detail' , 'shape' , 'name' , 'count' ] ) ; check ( 'letter' , redunt ) ; } else if ( has ( args , 'detail' ) ) { check ( [ 'shape' , 'name' , 'count' ] ) ; } else if ( has ( args , 'shape' ) ) { check ( [ 'name' , 'count' ] ) ; } else if ( has ( args , 'name' ) ) { check ( [ 'count' ] ) ; } } else { check ( [ 'detail' , 'shape' , 'name' , 'letter' ] ) ; check ( 'count' , redunt ) ; } } else if ( type === 'orderToWord' ) { if ( has ( args , 'match' ) ) { check ( [ 'matchorder' , 'contain' , 'start' ] ) ; } else if ( has ( args , 'matchorder' ) ) { check ( [ 'contain' , 'start' ] ) ; } else if ( has ( args , 'contain' ) ) { check ( [ 'start' ] ) ; } } else if ( type === 'strokeToWord' ) { } else if ( type === 'spellToWord' ) { } else if ( type === 'idiom' ) { if ( has ( args , 'spell' ) ) { check ( [ 'stroke' , 'char' ] ) ; } else if ( has ( args , 'stroke' ) ) { check ( [ 'tone' , 'char' ] ) ; } else { check ( [ 'tone' ] ) ; } } else if ( type === 'xhy' ) { } else if ( type === 'radical' ) { } warnArgs ( useless , '无效' , type , args ) ; warnArgs ( ignore , '被忽略' , type , args ) ; warnArgs ( redunt , '冗余' , type , args ) ; } ; function warnArgs ( arr , txt , type , args ) { if ( arr . length > 0 ) { let mes = `以下参数${ txt }:${ JSON . stringify ( arr ) };` ; if ( txt === '被忽略' && type === 'stroke' && ! has ( args , 'order' ) ) { mes += ' 要启用笔顺模式必须使用 order 参数' ; } else { mes += ` 可选值：[${ Object . keys ( ( _cnchar . type [ type ] as TypeValueObject ) ) }]` ; } _warn ( mes ) ; } } export function shapeSpell ( spell ) { const tones = '01234' ; if ( tones . indexOf ( spell [ spell . length - 1 ] ) === - 1 ) { return spell ; } return transformTone ( spell , true , 'low' ) . spell ; } export const transformTone = ( spell , needTone = false , type = 'low' ) : ITransformReturn => { if ( spell . indexOf ( 'v' ) !== - 1 ) { spell = spell . replace ( 'v' , 'ü' ) ; } const lastStr = spell [ spell . length - 1 ] ; let tone ; let index = - 1 ; let isTrans = false ; if ( parseInt ( lastStr ) . toString ( ) === lastStr ) { spell = spell . substr ( 0 , spell . length - 1 ) ; const info = spellInfo ( spell ) ; index = info . index ; tone = parseInt ( lastStr ) as ToneType ; isTrans = true ; if ( needTone ) { spell = setTone ( spell , index - 1 , tone ) ; } } else { const info = spellInfo ( spell ) ; index = info . index ; tone = info . tone ; if ( ! needTone && tone !== 0 ) { spell = info . spell ; } } if ( type === 'low' ) { spell = spell . toLowerCase ( ) ; } else if ( type === 'up' ) { spell = spell . toUpperCase ( ) ; } return { spell , tone , index , isTrans } ; } ;