@@ -152,8 +152,7 @@ ConfigEvent.subscribe((eventKey, eventValue, nosave) => {
152152 }
153153 if ( eventKey === "fontSize" && ! nosave ) {
154154 OutOfFocus . hide ( ) ;
155- updateWordsHeight ( true ) ;
156- void updateWordsInputPosition ( true ) ;
155+ updateWordWrapperClasses ( ) ;
157156 }
158157 if (
159158 [ "fontSize" , "fontFamily" , "blindMode" , "hideExtraLetters" ] . includes (
@@ -184,16 +183,7 @@ ConfigEvent.subscribe((eventKey, eventValue, nosave) => {
184183 updateWordWrapperClasses ( ) ;
185184 }
186185
187- if ( eventKey === "tapeMode" && ! nosave ) {
188- if ( eventValue === "off" ) {
189- $ ( "#words" ) . css ( "margin-left" , "unset" ) ;
190- } else {
191- scrollTape ( ) ;
192- }
193- updateLiveStatsMargin ( ) ;
194- }
195-
196- if ( eventKey === "tapeMargin" ) {
186+ if ( [ "tapeMode" , "tapeMargin" ] . includes ( eventKey ) ) {
197187 updateLiveStatsMargin ( ) ;
198188 }
199189
@@ -404,7 +394,8 @@ function updateWordWrapperClasses(): void {
404394 $ ( "#words" ) . attr ( "class" , existing . join ( " " ) ) ;
405395
406396 updateWordsWidth ( ) ;
407- updateWordsHeight ( true ) ;
397+ updateWordsWrapperHeight ( true ) ;
398+ updateWordsMargin ( ) ;
408399 setTimeout ( ( ) => {
409400 void updateWordsInputPosition ( true ) ;
410401 } , 250 ) ;
@@ -419,8 +410,7 @@ export function showWords(): void {
419410 wordsHTML += getWordHTML ( TestWords . words . get ( i ) ) ;
420411 }
421412 } else {
422- wordsHTML =
423- '<div class="word">word height</div><div class="word active"></div>' ;
413+ wordsHTML = '<div class="word active"></div>' ;
424414 }
425415
426416 $ ( "#words" ) . html ( wordsHTML ) ;
@@ -431,10 +421,6 @@ export function showWords(): void {
431421 } , 125 ) ;
432422
433423 updateWordWrapperClasses ( ) ;
434-
435- if ( Config . mode === "zen" ) {
436- $ ( document . querySelector ( ".word" ) as Element ) . remove ( ) ;
437- }
438424}
439425
440426const posUpdateLangList = [ "japanese" , "chinese" , "korean" ] ;
@@ -495,107 +481,83 @@ export async function updateWordsInputPosition(initial = false): Promise<void> {
495481 }
496482}
497483
498- function updateWordsHeight ( force = false ) : void {
484+ export function updateWordsWrapperHeight ( force = false ) : void {
499485 if ( ActivePage . get ( ) !== "test" || resultVisible ) return ;
500486 if ( ! force && Config . mode !== "custom" ) return ;
501- $ ( "#wordsWrapper" ) . removeClass ( "hidden" ) ;
502- const wordHeight = $ ( document . querySelector ( ".word" ) as Element ) . outerHeight (
503- true
504- ) as number ;
505- const wordsHeight = $ (
506- document . querySelector ( "#words" ) as Element
507- ) . outerHeight ( true ) as number ;
487+ const wrapperEl = document . getElementById ( "wordsWrapper" ) as HTMLElement ;
488+ const wordElements = wrapperEl . querySelectorAll < HTMLElement > ( "#words .word" ) ;
489+ const activeWordEl =
490+ wordElements [ TestState . activeWordIndex - activeWordElementOffset ] ;
491+ if ( ! activeWordEl ) return ;
492+
493+ wrapperEl . classList . remove ( "hidden" ) ;
494+
495+ //insert temporary character for zen mode
496+ const activeWordEmpty = activeWordEl ?. children . length === 0 ;
497+ if ( activeWordEmpty ) {
498+ activeWordEl . insertAdjacentHTML (
499+ "beforeend" ,
500+ '<letter style="opacity: 0;">_</letter>'
501+ ) ;
502+ }
503+ const wordComputedStyle = window . getComputedStyle ( activeWordEl ) ;
504+ const wordMargin =
505+ parseInt ( wordComputedStyle . marginTop ) +
506+ parseInt ( wordComputedStyle . marginBottom ) ;
507+ const wordHeight = activeWordEl . offsetHeight + wordMargin ;
508+
509+ let wrapperHeightStr : string ;
510+ let outOfFocusMargin : string | undefined = undefined ;
508511
509512 const shouldLimitToThreeLines =
510513 Config . mode === "time" ||
511514 ( Config . mode === "custom" && CustomText . getLimitMode ( ) === "time" ) ||
512- ( Config . mode === "custom" &&
513- CustomText . getLimitMode ( ) === "word" &&
514- CustomText . getLimitValue ( ) === 0 ) ||
515- ( Config . mode === "custom" &&
516- CustomText . getLimitMode ( ) === "section" &&
517- CustomText . getLimitValue ( ) === 0 ) ;
515+ ( Config . mode === "custom" && CustomText . getLimitValue ( ) === 0 ) ;
518516
519517 if ( Config . showAllLines && ! shouldLimitToThreeLines ) {
520- // overflow-x should not be visible in tape mode, but since showAllLines can't
521- // be enabled simultaneously with tape mode we don't need to check it's off
522- $ ( "#words" )
523- . css ( "height" , "auto" )
524- . css ( "overflow" , "visible clip" )
525- . css ( "width" , "100%" )
526- . css ( "margin-left" , "unset" ) ;
527- $ ( "#wordsWrapper" ) . css ( "height" , "auto" ) . css ( "overflow" , "visible clip" ) ;
528-
529- let nh = wordHeight * 3 ;
530-
531- if ( nh > wordsHeight ) {
532- nh = wordsHeight ;
533- }
534- $ ( ".outOfFocusWarning" ) . css (
535- "margin-top" ,
536- wordHeight + convertRemToPixels ( 1 ) / 2 + "px"
537- ) ;
518+ wrapperHeightStr = "auto" ;
519+ outOfFocusMargin = wordHeight + convertRemToPixels ( 1 ) / 2 + "px" ;
520+ } else if ( Config . tapeMode !== "off" ) {
521+ wrapperHeightStr = TestWords . hasNewline
522+ ? wordHeight * 3 + "px"
523+ : wordHeight * 1 + "px" ;
538524 } else {
539- let finalWordsHeight : number , finalWrapperHeight : number ;
540-
541- if ( Config . tapeMode !== "off" ) {
542- finalWordsHeight = wordHeight * 2 ;
543- finalWrapperHeight = wordHeight ;
544- } else {
545- let lines = 0 ;
546- let lastHeight = 0 ;
547- let wordIndex = 0 ;
548- const words = document . querySelectorAll ( "#words .word" ) ;
549- let wrapperHeight = 0 ;
550-
551- const wordComputedStyle = window . getComputedStyle ( words [ 0 ] as Element ) ;
552- const wordTopMargin = parseInt ( wordComputedStyle . marginTop ) ;
553- const wordBottomMargin = parseInt ( wordComputedStyle . marginBottom ) ;
554-
555- while ( lines < 3 ) {
556- const word = words [ wordIndex ] as HTMLElement | null ;
557- if ( ! word ) break ;
558- const height = word . offsetTop ;
559- if ( height > lastHeight ) {
560- lines ++ ;
561- wrapperHeight += word . offsetHeight + wordTopMargin + wordBottomMargin ;
562- lastHeight = height ;
563- }
564- wordIndex ++ ;
525+ let lines = 0 ;
526+ let lastTop = 0 ;
527+ let wordIndex = 0 ;
528+ let wrapperHeight = 0 ;
529+
530+ while ( lines < 3 ) {
531+ const word = wordElements [ wordIndex ] as HTMLElement | null ;
532+ if ( ! word ) break ;
533+ const top = word . offsetTop ;
534+ if ( top > lastTop ) {
535+ lines ++ ;
536+ wrapperHeight += word . offsetHeight + wordMargin ;
537+ lastTop = top ;
565538 }
566-
567- if ( lines < 3 ) wrapperHeight = wrapperHeight * ( 3 / lines ) ;
568-
569- const wordsHeight = ( wrapperHeight / 3 ) * 4 ;
570-
571- finalWordsHeight = wordsHeight ;
572- finalWrapperHeight = wrapperHeight ;
539+ wordIndex ++ ;
573540 }
541+ if ( lines < 3 ) wrapperHeight = wrapperHeight * ( 3 / lines ) ;
574542
575- // $("#words").css("height", "0px") ;
576- // not sure why this was here, wonder if removing it will break anything
543+ wrapperHeightStr = wrapperHeight + "px" ;
544+ }
577545
578- if ( Config . tapeMode !== "off" ) {
579- $ ( "#words" ) . css ( { overflow : "hidden" , width : "200vw" } ) ;
580- $ ( "#wordsWrapper" ) . css ( { overflow : "hidden" } ) ;
581- scrollTape ( ) ;
582- } else {
583- $ ( "#words" ) . css ( {
584- overflow : "visible clip" ,
585- marginLeft : "unset" ,
586- width : "" ,
587- } ) ;
588- $ ( "#wordsWrapper" ) . css ( { overflow : "visible clip" } ) ;
589- }
546+ wrapperEl . style . height = wrapperHeightStr ;
547+ if ( outOfFocusMargin !== undefined ) {
548+ $ ( ".outOfFocusWarning" ) . css ( { height : 0 , "margin-top" : outOfFocusMargin } ) ;
549+ }
590550
591- setTimeout ( ( ) => {
592- $ ( "#words" ) . css ( "height" , finalWordsHeight + "px" ) ;
593- $ ( "#wordsWrapper" ) . css ( "height" , finalWrapperHeight + "px" ) ;
594- $ ( ".outOfFocusWarning" ) . css (
595- "margin-top" ,
596- finalWrapperHeight / 2 - convertRemToPixels ( 1 ) / 2 + "px"
597- ) ;
598- } , 0 ) ;
551+ if ( activeWordEmpty ) {
552+ activeWordEl ?. replaceChildren ( ) ;
553+ }
554+ }
555+
556+ function updateWordsMargin ( ) : void {
557+ if ( Config . tapeMode !== "off" ) {
558+ scrollTape ( ) ;
559+ } else {
560+ setTimeout ( ( ) => $ ( "#words" ) . css ( "margin-left" , "unset" ) , 0 ) ;
599561 }
600562}
601563
@@ -1106,7 +1068,7 @@ export function lineJump(currentTop: number): void {
11061068 const wordsWrapperWidth = (
11071069 document . querySelector ( "#wordsWrapper" ) as HTMLElement
11081070 ) . offsetWidth ;
1109- const newMargin = wordsWrapperWidth / 2 ;
1071+ const newMargin = wordsWrapperWidth * ( Config . tapeMargin / 100 ) ;
11101072 newCss [ "marginLeft" ] = `${ newMargin } px` ;
11111073 }
11121074 currentLinesAnimating ++ ;
@@ -1136,7 +1098,7 @@ export function lineJump(currentTop: number): void {
11361098 }
11371099 }
11381100 currentTestLine ++ ;
1139- updateWordsHeight ( ) ;
1101+ updateWordsWrapperHeight ( ) ;
11401102}
11411103
11421104export function setRightToLeft ( isEnabled : boolean ) : void {
0 commit comments