@@ -139,7 +139,7 @@ const debouncedZipfCheck = debounce(250, async () => {
139
139
}
140
140
} ) ;
141
141
142
- ConfigEvent . subscribe ( ( eventKey , eventValue , nosave ) => {
142
+ ConfigEvent . subscribe ( async ( eventKey , eventValue , nosave ) => {
143
143
if (
144
144
( eventKey === "language" || eventKey === "funbox" ) &&
145
145
Config . funbox . split ( "#" ) . includes ( "zipf" )
@@ -149,7 +149,7 @@ ConfigEvent.subscribe((eventKey, eventValue, nosave) => {
149
149
if ( eventKey === "fontSize" && ! nosave ) {
150
150
OutOfFocus . hide ( ) ;
151
151
updateWordsWrapperHeight ( true ) ;
152
- updateWordsMargin ( ) ;
152
+ await updateWordsMargin ( ) ;
153
153
void updateWordsInputPosition ( true ) ;
154
154
}
155
155
if (
@@ -166,7 +166,7 @@ ConfigEvent.subscribe((eventKey, eventValue, nosave) => {
166
166
167
167
if ( eventValue === undefined ) return ;
168
168
if ( eventKey === "highlightMode" ) {
169
- if ( ActivePage . get ( ) === "test" ) updateActiveElement ( ) ;
169
+ if ( ActivePage . get ( ) === "test" ) await updateActiveElement ( ) ;
170
170
}
171
171
172
172
if (
@@ -178,7 +178,7 @@ ConfigEvent.subscribe((eventKey, eventValue, nosave) => {
178
178
"hideExtraLetters" ,
179
179
] . includes ( eventKey )
180
180
) {
181
- updateWordWrapperClasses ( ) ;
181
+ await updateWordWrapperClasses ( ) ;
182
182
}
183
183
184
184
if ( eventKey === "showAllLines" ) {
@@ -246,10 +246,10 @@ export function blurWords(): void {
246
246
$ ( "#wordsInput" ) . trigger ( "blur" ) ;
247
247
}
248
248
249
- export function updateActiveElement (
249
+ export async function updateActiveElement (
250
250
backspace ?: boolean ,
251
251
initial = false
252
- ) : void {
252
+ ) : Promise < void > {
253
253
const active = document . querySelector ( "#words .active" ) ;
254
254
if ( ! backspace ) {
255
255
active ?. classList . add ( "typed" ) ;
@@ -277,7 +277,7 @@ export function updateActiveElement(
277
277
void updateWordsInputPosition ( ) ;
278
278
}
279
279
if ( ! initial && Config . tapeMode !== "off" ) {
280
- scrollTape ( ) ;
280
+ await scrollTape ( ) ;
281
281
}
282
282
}
283
283
@@ -353,16 +353,18 @@ function getWordHTML(word: string): string {
353
353
return retval ;
354
354
}
355
355
356
- function updateWordsMargin ( ) : void {
356
+ async function updateWordsMargin ( ) : Promise < void > {
357
357
if ( Config . tapeMode !== "off" ) {
358
- scrollTape ( true ) ;
358
+ await scrollTape ( true ) ;
359
359
} else {
360
- $ ( "#words" ) . css ( "margin-left" , "unset" ) ;
361
- $ ( ".afterNewline" ) . css ( "margin-left" , "unset" ) ;
360
+ setTimeout ( ( ) => {
361
+ $ ( "#words" ) . css ( "margin-left" , "unset" ) ;
362
+ $ ( ".afterNewline" ) . css ( "margin-left" , "unset" ) ;
363
+ } , 125 ) ;
362
364
}
363
365
}
364
366
365
- function updateWordWrapperClasses ( ) : void {
367
+ async function updateWordWrapperClasses ( ) : Promise < void > {
366
368
if ( Config . tapeMode !== "off" ) {
367
369
$ ( "#words" ) . addClass ( "tape" ) ;
368
370
$ ( "#wordsWrapper" ) . addClass ( "tape" ) ;
@@ -408,13 +410,13 @@ function updateWordWrapperClasses(): void {
408
410
409
411
updateWordsWidth ( ) ;
410
412
updateWordsWrapperHeight ( true ) ;
411
- updateWordsMargin ( ) ;
413
+ await updateWordsMargin ( ) ;
412
414
setTimeout ( ( ) => {
413
415
void updateWordsInputPosition ( true ) ;
414
416
} , 250 ) ;
415
417
}
416
418
417
- export function showWords ( ) : void {
419
+ export async function showWords ( ) : Promise < void > {
418
420
$ ( "#words" ) . empty ( ) ;
419
421
420
422
let wordsHTML = "" ;
@@ -428,12 +430,12 @@ export function showWords(): void {
428
430
429
431
$ ( "#words" ) . html ( wordsHTML ) ;
430
432
431
- updateActiveElement ( undefined , true ) ;
433
+ await updateActiveElement ( undefined , true ) ;
432
434
setTimeout ( ( ) => {
433
435
void Caret . updatePosition ( ) ;
434
436
} , 125 ) ;
435
437
436
- updateWordWrapperClasses ( ) ;
438
+ await updateWordWrapperClasses ( ) ;
437
439
}
438
440
439
441
const posUpdateLangList = [ "japanese" , "chinese" , "korean" ] ;
@@ -917,17 +919,20 @@ export async function updateActiveWordLetters(
917
919
"<div class='beforeNewline'></div><div class='newline'></div><div class='afterNewline'></div>"
918
920
) ;
919
921
if ( Config . tapeMode !== "off" ) {
920
- scrollTape ( ) ;
922
+ await scrollTape ( ) ;
921
923
}
922
924
}
923
925
924
926
let allowWordRemoval = true ;
925
- export function scrollTape ( noRemove = false ) : void {
927
+ export async function scrollTape ( noRemove = false ) : Promise < void > {
926
928
if ( ActivePage . get ( ) !== "test" || resultVisible ) return ;
927
929
928
930
const waitForLineJumpAnimation = lineTransition && ! allowWordRemoval ;
929
931
if ( waitForLineJumpAnimation ) noRemove = true ;
930
932
933
+ const currentLang = await JSONData . getCurrentLanguage ( Config . language ) ;
934
+ const isLanguageRTL = currentLang . rightToLeft ;
935
+
931
936
const wordsWrapperWidth = (
932
937
document . querySelector ( "#wordsWrapper" ) as HTMLElement
933
938
) . offsetWidth ;
@@ -997,7 +1002,11 @@ export function scrollTape(noRemove = false): void {
997
1002
const wordOuterWidth = $ ( child ) . outerWidth ( true ) ?? 0 ;
998
1003
const forWordLeft = Math . floor ( child . offsetLeft ) ;
999
1004
const forWordWidth = Math . floor ( child . offsetWidth ) ;
1000
- if ( ! noRemove && forWordLeft < 0 - forWordWidth ) {
1005
+ if (
1006
+ ! noRemove &&
1007
+ ( ( ! isLanguageRTL && forWordLeft < 0 - forWordWidth ) ||
1008
+ ( isLanguageRTL && forWordLeft > wordsWrapperWidth ) )
1009
+ ) {
1001
1010
toRemove . push ( child ) ;
1002
1011
widthToRemove += wordOuterWidth ;
1003
1012
wordsToRemoveCount ++ ;
@@ -1025,30 +1034,44 @@ export function scrollTape(noRemove = false): void {
1025
1034
const currentChildMargin = parseInt ( element . style . marginLeft ) || 0 ;
1026
1035
element . style . marginLeft = `${ currentChildMargin - widthToRemove } px` ;
1027
1036
}
1037
+ if ( isLanguageRTL ) widthToRemove *= - 1 ;
1028
1038
const currentWordsMargin = parseInt ( wordsEl . style . marginLeft ) || 0 ;
1029
1039
wordsEl . style . marginLeft = `${ currentWordsMargin + widthToRemove } px` ;
1030
1040
}
1031
1041
1042
+ const inputLength = TestInput . input . current . length ;
1032
1043
let currentWordWidth = 0 ;
1033
- if ( Config . tapeMode === "letter" ) {
1034
- if ( TestInput . input . current . length > 0 ) {
1035
- const letters = activeWordEl . querySelectorAll ( "letter" ) ;
1036
- for ( let i = 0 ; i < TestInput . input . current . length ; i ++ ) {
1037
- const letter = letters [ i ] as HTMLElement ;
1038
- if (
1039
- ( Config . blindMode || Config . hideExtraLetters ) &&
1040
- letter . classList . contains ( "extra" )
1041
- ) {
1042
- continue ;
1043
- }
1044
- currentWordWidth += $ ( letter ) . outerWidth ( true ) ?? 0 ;
1044
+ if ( Config . tapeMode === "letter" && inputLength > 0 ) {
1045
+ const letters = activeWordEl . querySelectorAll ( "letter" ) ;
1046
+ let lastPositiveLetterWidth = 0 ;
1047
+ for ( let i = 0 ; i < inputLength ; i ++ ) {
1048
+ const letter = letters [ i ] as HTMLElement ;
1049
+ if (
1050
+ ( Config . blindMode || Config . hideExtraLetters ) &&
1051
+ letter . classList . contains ( "extra" )
1052
+ ) {
1053
+ continue ;
1045
1054
}
1055
+ const letterOuterWidth = $ ( letter ) . outerWidth ( true ) ?? 0 ;
1056
+ currentWordWidth += letterOuterWidth ;
1057
+ if ( letterOuterWidth > 0 ) lastPositiveLetterWidth = letterOuterWidth ;
1046
1058
}
1047
- }
1048
- let newMargin =
1049
- wordsWrapperWidth / 2 - ( wordsWidthBeforeActive + currentWordWidth ) ;
1059
+ // if current letter has zero width move the tape to previous positive width letter
1060
+ if ( $ ( letters [ inputLength ] as Element ) . outerWidth ( true ) === 0 )
1061
+ currentWordWidth -= lastPositiveLetterWidth ;
1062
+ }
1063
+
1064
+ let newMargin = wordsWrapperWidth / 2 ;
1065
+ if ( isLanguageRTL )
1066
+ newMargin +=
1067
+ wordsWidthBeforeActive +
1068
+ currentWordWidth -
1069
+ wordsEl . offsetWidth +
1070
+ wordRightMargin ;
1071
+ else newMargin -= wordsWidthBeforeActive + currentWordWidth ;
1050
1072
if ( waitForLineJumpAnimation )
1051
1073
newMargin = parseInt ( wordsEl . style . marginLeft ) || 0 ;
1074
+
1052
1075
const jqWords = $ ( wordsEl ) ;
1053
1076
if ( Config . smoothLineScroll ) {
1054
1077
jqWords . stop ( "leftMargin" , true , false ) . animate (
@@ -1058,10 +1081,10 @@ export function scrollTape(noRemove = false): void {
1058
1081
{
1059
1082
duration : SlowTimer . get ( ) ? 0 : 125 ,
1060
1083
queue : "leftMargin" ,
1061
- complete : ( ) => {
1084
+ complete : async ( ) => {
1062
1085
if ( noRemove ) {
1063
- if ( waitForLineJumpAnimation ) scrollTape ( true ) ;
1064
- else scrollTape ( ) ;
1086
+ if ( waitForLineJumpAnimation ) await scrollTape ( true ) ;
1087
+ else await scrollTape ( ) ;
1065
1088
}
1066
1089
} ,
1067
1090
}
@@ -1082,7 +1105,7 @@ export function scrollTape(noRemove = false): void {
1082
1105
linesWidths . forEach ( ( width , index ) => {
1083
1106
( afterNewLineEls [ index ] as HTMLElement ) . style . marginLeft = `${ width } px` ;
1084
1107
} ) ;
1085
- if ( noRemove ) scrollTape ( ) ;
1108
+ if ( noRemove ) await scrollTape ( ) ;
1086
1109
}
1087
1110
}
1088
1111
0 commit comments