Operation Unz̖̬̜̺̬a͇͖̯͔͉l̟̭g͕̝̼͇͓̪͍o̬̝͍̹̻ I'm sure you're all familiar with Z̃͗̇̚͟Ḁ̬̹̈̊̂̏̚L̜̼͊ͣ̈́̿̚G̱̮ͩ̃͑̆ͤ̂̚Õ̷͇͉̺̜̲ͩ́ͪͬͦ͐ ̪̀ͤͨ͛̍̈͢ĝ̭͇̻̊ͮ̾͂e̬̤͔̩̋ͮ̊̈ͭ̓̃n͖͎̘̭̯̳͎͒͂̏̃̾ͯe͕̖̋ͧ͑ͪ̑r̛ͩa̴͕̥̺̺̫̾ͭ͂ͥ̄ͧ͆t͍̻̘̆o͓̥ͤͫ̃̈̂r̹̤͇̰̻̯̐ͮ̈́ͦ͂͞. If not, you can play with the classical generator a bit. Zalgo text is unicode text that started as English text and had a bunch of combining characters to make it hard to read artistic.

This is operation unzalgo. The object is to undo the generate zalgo text operation. Since only combining characters were added, only combining characters have to be removed. We're only going to be operating on English, because of course we are. However, all proper English text must to come through unmutated. That is, if you were fed proper English, you must output your input. Sounds easy, right? Not so fast. English has combining characters, just not very many, and people are slowly forgetting they exist.

The rules:

1) You may either strip combining characters or filter out all invalid characters, at your discretion. This doesn't have to work at all languages because the operation itself makes no sense if we started with something that wasn't English text. Abandon hope all ye who enter here.

2) You may assume that unicode normalization was not executed after zalgo generation; that is all added combining characters are still combining characters.

3) All printable ASCII characters (codepoints between 0x20 and 0x7E inclusive) must survive, as well as tab (0x09), and newline (0x0A).

4) English has diaresis over vowels. These must survive whether expressed as natural characters or combining characters.

Vowel table (Each group of three is in the form (unmodified character, single character, combining characters):

a ä ä A Ä Ä
e ë ë E Ë Ë
i ï ï I Ï Ï
o ö ö O Ö Ö
u ü ü U Ü Ü
y ÿ ÿ Y Ÿ Ÿ


The combining diaresis character is code-point 0x308.

5) English does not have diaeresis over consonants. You must dump them when constructed as combining characters.

6) British English has four ligatures. These must survive:

æ Æ œ Œ


7) The following symbols must survive:

Editor substitution table (includes smart quotes in both directions):

… ¼ ½ ¾ ‘ ’ “ ” ™


Symbol table:

$ ¢ £ ¬ † ‡ • ‰ · ° ± ÷


8) If you get something like ö̎̋̉͆̉ö͒̿̍ͨͦ̽, both oo and öö are valid answers.

9) Input may contain both zalgo and non-zalgo characters; and the non-zalgo characters should be unmodified: if somebody sends 'cöoperate with dͧḯ̍̑̊͐sc͆͐orͩ͌ͮ̎ͬd̄̚', they should still get back 'cöoperate with discord' not 'cooperate with discord'.

10) If any character is not specified, it doesn't matter what you do with it. Feel free to use this rule to lossily compress the keep-drop rules.

11) Your program must handle all unicode codepoints as input. No fair specifying a code-page that trivializes the problem.

Additional test cases:

"z̈ ỏ"        "z o"
"r̈ëën̈ẗr̈ÿ"    "rëëntrÿ"  (don't outsmart yourself)


I have been informed  this case is also excellent but it's starting point contains a few characters with unspecified behavior. If you maul Θ, Ό, Ɲ, or ȳ I don't care.

A pretty comprehensive test input:

    cöoperate with dͧḯ̍̑̊͐sc͆͐orͩ͌ͮ̎ͬd̄̚ æ Æ œ Œ…¼½¾‘’“”™$¢£¬†‡•‰·°±÷

a ä ä A Ä Ä
e ë ë E Ë Ë
i ï ï I Ï Ï   Z̃͗̇̚͟Ḁ̬̹̈̊̂̏̚L̜̼͊ͣ̈́̿̚G̱̮ͩ̃͑̆ͤ̂̚Õ̷͇͉̺̜̲ͩ́ͪͬͦ͐ ̪̀ͤͨ͛̍̈͢ĝ̭͇̻̊ͮ̾͂e̬̤͔̩̋ͮ̊̈ͭ̓̃n͖͎̘̭̯̳͎͒͂̏̃̾ͯe͕̖̋ͧ͑ͪ̑r̛ͩa̴͕̥̺̺̫̾ͭ͂ͥ̄ͧ͆t͍̻̘̆o͓̥ͤͫ̃̈̂r̹̤͇̰̻̯̐ͮ̈́ͦ͂͞
o ö ö O Ö Ö
u ü ü U Ü Ü
y ÿ ÿ Y Ÿ Ÿ
Unz̖̬̜̺̬a͇͖̯͔͉l̟̭g͕̝̼͇͓̪͍o̬̝͍̹̻


ö̎̋̉͆̉ö͒̿̍ͨͦ̽
 !"#$%&amp;'()*+,-./0123456789:;&lt;=&gt;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅ
JavaScript (Node.js), 45 bytes



s=&gt;s.replace(/([aeiouy]̈)|[̀-ͯ҉]/ig,'$1')


Try it online!

More readable version:

s=&gt;s.replace(/([aeiouy]\u0308)|[\u0300-\u036f\u0489]/ig,'$1')