hisyëö syllabary

Newest candidate for Koqatha's official writing system.

by MadcapJake

HTML

<div class="inWrapper">
  <label>Latin:</label>
  <textarea id="latinIn" cols="80" rows="3" spellcheck="false">
    zowo hônyo cûtî bûlo fîkî ulyô socok xe kôs zoston mût boxô dîzel yê zowo legêfû onô.
  </textarea>
  <button id="copyHisyeo" type="button">Copy 👇</button>
</div>
<div class="hidden" id="hisyeoOutText"></div>
<div id="hisyeoOut"></div>

CSS

@import url('https://fonts.cdnfonts.com/css/charis-sil');
@import url('https://fonts.cdnfonts.com/css/doulos-sil');
@import url('https://fonts.cdnfonts.com/css/gentium-plus');
@import url('https://fonts.cdnfonts.com/css/noto-sans');

* {
  /* font-family: 'Charis SIL', sans-serif; */
  /* font-family: 'Gentium Plus', sans-serif; */
  font-family: "Noto Sans", sans-serif;
  /* font-family: "Arial", sans-serif; */
}

.inWrapper {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

#hisyeoOut, #latinOut {
  margin-top: 5px;
  padding: 3px;
  border: 2px solid black;
  min-width: 400px;
  min-height: 20px;
  border-radius: 5px;
  font-size: 2em;
  background-color: white;
}


.hidden {
  display: none;
}

label {
  padding-right: 20px;
}

button {
  margin: 3px;
  color: black;
}

button.flash {
  animation: flash 1s linear forwards;
}

@keyframes flash {
 0% { color: red; }
 100% { color: black; } 
}

JavaScript

const lCons =  ' hkgtcsxdzbfmnwly', sCons = ' hkgtcsxdzbfmnwly',
      lVowels = 'ôuûiîeê', sVowels = 'orujiqe',
      lDiacritics = 'ktlnsc', sDiacritics = 'KTLNSC'
      index = (loc, val) => loc.indexOf(val.toLowerCase());

latinIn.oninput = function (e) {
    hisyeoOutText.innerText = latinIn.value
        // Pre-process quotation marks, replacing with [ and ] as placeholder quotation marks
        .split('"')
        .reduce((acc, v, i) => acc + (i % 2 ? '[' : ']') + v)
        // Pre-process proper nouns, adding { and } as placeholder transliteration marks
        .replace(/\b[A-ZÔÊÎÛ][A-ZÔÊÎÛa-zôêîûꞌ']*(?: [A-ZÔÊÎÛ][A-ZÔÊÎÛa-zôêîûꞌ']*)*/g, '{$&}')
        // Replace abugida characters
        .replace(
            /([hkgtcsxdzbfmnwly])o(?=[oôuûiîeê])|([hkgtcsxdzbfmnwly])?([oôuûiîeê])([ktlnsc](?![oôuûiîeê]))?|([hkgtcsxdzbfmnwly])(?![oôuûiîeê])/gi, // RegEx: cons?+vowel+n? | const
            (_, p0, p1, p2, p3, p4) => {
                // console.log(`p0: ${p0}\tp1: ${p1}\tp2: ${p2}\tp3: ${p3}\tp4: ${p4}`)
                return p0 ? sCons[index(lCons, p0)] + 'A' :             // default vowel saver
                  p4 ? sCons[index(lCons, p4)] :                        // null onset
                    (sCons[(p1 ? index(lCons, p1) : -1)] ?? '') +       // onset
                    (sVowels[index(lVowels, p2)] ?? (!p1 ? 'a' : '')) + // vowel
                    (p3 ? sDiacritics[index(lDiacritics, p3)] : '')     // coda
            }
                      
        )
        // Replace punctuation
        .replace(/\[/g, '«')
        .replace(/]/g, '»')
        // Replace newlines with a placeholder character to later update as <br />
        // because this initial step uses innerText for safer DOM modification
        .replace(/\r?\n/g, '\uFFFE');
    // Update actual output through innerHTML and only now update line breaks
    hisyeoOut.innerHTML = hisyeoOutText.innerHTML.replace(/\uFFFE/g,...