From a0e6ad996742bceb51eb281bf02ef5c020b0f205 Mon Sep 17 00:00:00 2001 From: Jonathan Olson Date: Wed, 7 Feb 2024 20:42:39 -0700 Subject: [PATCH] Improved workaround to how Safari badly treats embedding marks, see https://github.com/phetsims/website-meteor/issues/656 --- js/util/Utils.ts | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/js/util/Utils.ts b/js/util/Utils.ts index 931f954a3..101285e15 100644 --- a/js/util/Utils.ts +++ b/js/util/Utils.ts @@ -556,9 +556,30 @@ const Utils = { */ safariEmbeddingMarkWorkaround( str: string ): string { if ( platform.safari ) { - // Add in zero-width spaces for Safari, so it doesn't have adjacent embedding marks ever (which seems to prevent - // things). - return str.split( '' ).join( '\u200B' ); + // NOTE: I don't believe it's likely/possible a valid UTF-8 string will contain these code points adjacently, + // due to the property that you can start reading UTF-8 from any byte. So we're safe to split it and break it + // into UTF-16 code units, since we're not mucking with surrogate pairs. + const utf16CodeUnits = str.split( '' ); + let result = ''; + + // NOTE: We're only inserting zero-width spaces between embedding marks, since prior to this our insertion between + // certain code points was causing issues with Safari (https://github.com/phetsims/website-meteor/issues/656) + let lastIsEmbeddingMark = false; + for ( let i = 0; i < utf16CodeUnits.length; i++ ) { + const next = utf16CodeUnits[ i ]; + const nextIsEmbeddingMark = next === '\u202a' || next === '\u202b' || next === '\u202c'; + + // Add in zero-width spaces for Safari, so it doesn't have adjacent embedding marks ever (which seems to prevent + // things). + if ( lastIsEmbeddingMark && nextIsEmbeddingMark ) { + result += '\u200B'; + } + result += next; + + lastIsEmbeddingMark = nextIsEmbeddingMark; + } + + return result; } else { return str;