User:Shenme/BetaCodeIME.js

var BetaCodeElVersion = "0.12" // 20211105

/*

Changes for version 0.12

* Easier user-specific configuration

Users can specify their preferred configuration in their common.js    before including the Beta Code IME script. See documentation for creating user config BetaCodeElUserConfig

* Allow user enabling of composition status display features.

Configuration control of floating in-progress status box, inline in-progress status and whether the inline status should include in-progress Greek character and/or Beta Code input characters.

* Add feature for inline display of in-progress status.

Some users may prefer seeing composition state inline within the text box.

* Add ability to force composition completion with tab key.

Correcting existing text often means forcing a vowel to 'finish'. Doing so by entering a space or period then means the user has to backspace and delete the extra unneeded character. Instead, while a Greek vowel composition is in-progress, use the tab key to complete the vowel.

* Add ability to discard in-progress composition with backspace key.

Previously it was uncertain or clumsy to delete an in-progress composition. While composition is in-progress, a backspace key will remove the incomplete composition.

* Allow arbitrary activation match strings

Changes for version 0.11 * Now positioning the status box using CSS 'fixed' positioning, which allows the user to 'grab' and reposition the status box within the browser window - this is sometimes necessary to avoid covering up text while editing. * Expanded status box to encompass the worst-case for vertically stacking diacritics. * Removed the Greek/Coptic mode indication as we're abandoning Coptic/Uncials for now. * Fancy background for status box (ala Greek flag)



// IIFE is best way to handle ES5 requirement of Wikimedia var BetaCodeElProcessor = (function {

// Ancient Greek diacritics are weird. //   Beta Code is weird. //     The perfect marriage. // // Beta Code is a standard for encoding polytonic Greek, using only ASCII // characters. Beta Code has become "a sort of universal default keymap // for text entry in polytonic Greek". // // Using only basic keyboard input the complicated accented Greek letters // may be entered more easily, without having to remember complex and // difficult key 'chords' required by operating system input methods. // // While switched into Beta code mode, the plain alphabetic keys are // remapped to enter Greek letters. The keys and Greek letters are matched // using two general guides. // // Alphabet letters that have 'obvious' relationships are paired, such that // 'a' gives you 'α', 'b' to 'β', 'g' to 'γ' (gamma), etc.  That gives us //  most of the needed Greek letters. // // The remaining few are paired up by looking at the 'shape' of the letters // while wearing really bad glasses. Since omicron already uses the 'o' key, // Beta code chooses 'w' for 'ω' omega, because they look alike. Similarly // for 'Θ' theta the letter 'Q' is chosen. Look for a couple other strange // choices in the list of correspondences below. // //            Input Keys         Greek Letter //            *A    A     ->     Α    α    ALPHA //            *B    B     ->     Β    β    BETA //            *G    G     ->     Γ    γ    GAMMA //            *D    D     ->     Δ    δ    DELTA //            *E    E     ->     Ε    ε    EPSILON //            *V    V     ->     Ϝ    ϝ    DIGAMMA Ϝ  ϝ //            *Z    Z     ->     Ζ    ζ    ZETA //            *H    H     ->     Η    η    ETA //            *Q    Q     ->     Θ    θ    THETA //            *I    I     ->     Ι    ι    IOTA //            *K    K     ->     Κ    κ    KAPPA //            *L    L     ->     Λ    λ    LAMDA //            *M    M     ->     Μ    μ    MU          //             *N    N     ->     Ν    ν    NU          //             *C    C     ->     Ξ    ξ    XI          //             *O    O     ->     Ο    ο    OMICRON //            *P    P     ->     Π    π    PI          //             *R    R     ->     Ρ    ρ    RHO //            *S    S     ->     Σ    σ    SIGMA //                  S     ->          ς    FINAL SIGMA //            *T    T     ->     Τ    τ    TAU //            *U    U     ->     Υ    υ    UPSILON //            *F    F     ->     Φ    φ    PHI //            *X    X     ->     Χ    χ    CHI //            *Y    Y     ->     Ψ    ψ    PSI //            *W    W     ->     Ω    ω    OMEGA // // As usual with any too simple conventions there are a number of added // rules to allow for special cases. // // When strict Beta Code mode is enabled, a capital Greek letter is requested // by first entering an asterisk before the letter key: *L -> Λ // // We default to loose mode where you can use upper- and lowercase letter keys // to indicate upper- and lowercase Greek letters: L -> Λ, l -> λ //     // These inputs   *S  *S3  S  S1  S2  S3   result in these chars: // // There are three forms for Greek 'S', the capital Sigma Σ, the middle // sigma σ, and the final end-of-word sigma form ς. // // The Greek FINAL SIGMA can usually be determined contextually, when a // 'σ' is found to be at the end of a word. Entering Beta Code text "PRO/S " // will be seen to result in "πρός ". However if this doesn't work sometime // you can enter: //          *S1  ->  Σ    SIGMA //           S1  ->  σ    MEDIAL SIGMA //           S2  ->  ς    FINAL SIGMA //          // Actually there are 2 more 'S', the LUNATE SIGMAs. To enter those you can // use: //          *S3  ->  Ϲ    LUNATE SIGMA   Ϲ //           S3  ->  ϲ    LUNATE SIGMA  	ϲ // to input this rarer form of SIGMA. // // A semi-standard extension to Beta code allows requesting FINAL SIGMA ς // by entering the alpha letter 'j'. // // // Diacritics //   Greek    Beta // Diacritic  Code       Name            Examples    Beta Coded as //       ̓       )     Smooth breathing      ἐν        E)N             PSILI //      ̔       (     Rough breathing       ὁ, οἱ     O(, OI(         DASIA         //       ́       /     Acute accent          πρός      PRO/S           OXIA          //       ͂       =     Circumflex accent     τῶν       TW=N            PERISPOMENI   //       ̀       \     Grave accent          πρὸς      PRO\S           VARIA         //       ͅ       |     Iota subscript        τῷ        TW=|            YPOGEGRAMMENI // // Less frequently needed // //       ̈       +     Diaeresis             προϊέναι  PROI+E/NAI      DIALYTIKA   //       ̄       &     macron                μαχαίρᾱς  MAXAI/RA&S      MACRON      //       ̆       '     Breve                 μάχαιρᾰ   MA/XAIRA'       BREVE       // // // // Punctuation //XXX So why do we have " "  "."  ","  "-"  ";"  here, if no change? //      "\u037E":   "\u003B",                   //  " ; "  "GREEK QUESTION MARK"                                     SEMICOLON //     "\u0387":   "\u00B7",                   //  " · "  "GREEK ANO TELEIA"                                        MIDDLE DOT // // Greek and ancient Greek also have uniquely Greek punctuation characters. // //          Greek //       Punctuation  Beta Code  Name //            ·          :      Colon aka Greek semicolon      (0387 -> 00B7) //                                 aka MIDDOT aka Interpunct //                                 aka "Ano Teleia" aka "Ano Stigme" //                                   //             ’          '      Apostrophe                 2019     ’ //                                 U+2019  ’  e2 80 99  RIGHT SINGLE QUOTATION MARK  &rsquo; // //            —          _      Dash from underline        2014 //                                 U+2014  —  e2 80 94  EM DASH   &mdash; // //            ʹ          #      Numeral (Keraia)   ca b9           ʹ //                                 U+02B9  ʹ  ca b9  MODIFIER LETTER PRIME // // Unchanging? //            ;          ;      Greek question Mark            (037E -> 003B) //            .          .      Period              002E //           ,          ,      Comma               002C //            ‐          -      Hyphen              2010   but 002D //            U+2010  ‐  e2 80 90  HYPHEN // // let betaPunctKeyOutputChars = { //// " ":  " ",        //  Space //// ".":  ".",        //  Period. 002E //// ",":  ",",        //  Comma              , 002C //   ":":  "\u00B7",   //  Colon (Ano Stigme)  · 00B7  Mid-dot //?? ";":  ";",        //  Greek Question Mark ; 003B //   "'":  "\u2019",   //  Apostrophe          ’ 2019  RIGHT SINGLE QUOTATION MARK  &rsquo; //?? "-":  "-",        //  Hyphen              ‐ 2010    - 002D //   "—":  "\u2014",   //  Dash                — 2014  EM DASH           &mdash; //   "#":  "\u02B9",   //  Keraia              # ʹ   0023 NUMBER SIGN //                     //                            02B9 MODIFIER LETTER PRIME //                     //                            0374 GREEK NUMERAL SIGN // } // //          https://en.wikipedia.org/wiki/Beta_Code#Punctuation // // U+037E ; GREEK QUESTION MARK   ->   U+003B ; SEMICOLON // U+0387 · GREEK ANO TELEIA      ->   U+00B7 · MIDDLE DOT // U+002D - HYPHEN-MINUS // U+2010 // U+2010 ‐ HYPHEN (HTML &dash; or &hyphen;) // // // References regarding Beta Code: //     https://en.wikipedia.org/wiki/Beta_Code //     http://stephanus.tlg.uci.edu/encoding.php //     http://stephanus.tlg.uci.edu/encoding/quickbeta.pdf   2016 //     http://stephanus.tlg.uci.edu/encoding/BCM.pdf         2016 //     http://www.tlg.uci.edu/encoding/precomposed.pdf // // References regarding Greek letters in Unicode: //     https://www.unicode.org/charts/PDF/U0300.pdf    (combining diacritics) //     https://www.unicode.org/charts/PDF/U0370.pdf    (first Greek block) //     https://www.unicode.org/charts/PDF/U1F00.pdf    (the Greek Extended block) // // Notes: // // Much of this code assumes only BMP Unicode chars are processed. // Handling surrogate pairs is difficult in Javascript. // // // //TODO // XXX have I done 'J'? Do I want to do 'J'? //     Do I want to enable 'J' *only* when in loose mode? // // XXX Do I need to document which Unicode codes are used? // // XXX Do I need to make diacritic key characters configurable? // // XXX Do I need to make alphabet key remapping configurable? // // XXX Do I need to make punctuation key remapping configurable? // //XXX Make note of the 2016 Unicode deprecation of "WITH OXIA" characters? //

//=====================================================================

// User-updatable options controlling processing of Beta code input // and generation of Greek output.

var optionsDefaults = { // The Beta code standard defines a strict ordering for the diacritic // characters that can follow vowels. Thus requiring breathing marks // before accents before iota, e.g. "W(=|". Additionally Beta code  // specifies use of an asterisk prefix to identify a capital letter,  // e.g. "*G" for 'Γ'.  Most implementations of Beta code have much  // looser rules.  //  // Whether vowel diacritics must be input in strict beta code order,  // and both uppercase and lowercase letters may be used.

isStrict:    false,

// All but the simplest Greek letters can be added to text in one of // two formats: precomposed or base letter plus combining characters. // // Every Greek letter with diacritics can be found as a 'precomposed' // Unicode character. The single code point value that is displayed as // the base letter and accents combined. This provided that the font // being used has those precomposed display characters defined within it. // // Unicode also defines sequences of combining characters, each // character of which contributes one accent/diacritic addition to the // base letter. This is supposed to be more flexible, permitting even // fonts that don't have all precomposed Greek letters in the "Greek // Extended" character block to display the intended letter plus accents. // However, sometimes the font and operating system don't produce a very // good result for display. // // Set this to true to ask only for precomposed Greek Extended block // characters, without depending on the system software to get all // compositions correctly displayed. If displayed characters don't look // 'right', try setting to true. If displayed characters don't all appear, // then maybe the font doesn't have all the needed precomposed characters, // and you should try setting this to false.

preferPrecomposed: true,

// Whether Greek precomposed characters are preferred chosen from the // lower Greek Unicode range 0370-03FF or from the upper Greek Extended // range 1F00-1FFF. The history is complicated, but it seems that now // precomposed characters from the lower range are 'standard'. //     https://wiki.digitalclassicist.org/Greek_Unicode_duplicated_vowels //       The latest versions of Unicode (as of 2016) have now formally //       deprecated and removed the vowel+oxia combinations from the //       Greek extended range, leaving only the vowel+tonos from the //       basic Greek and Coptic range. //     https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.184.3065&rep=rep1&type=pdf // When a particular Greek precomposed character is found in both Greek // character blocks, choose the code from the lower Unicode range 0370-03FF.

preferGreekExtended: false,

}

//=====================================================================

// Our constructor function, called with new xxx

function GreekBeta(callerOptions) { var options = Object.assign({}, optionsDefaults, callerOptions || {})

this.isStrict = options.isStrict this.preferPrecomposed = options.preferPrecomposed this.preferGreekExtended = options.preferGreekExtended

// One of 'reset', 'rSeen', 'sSeen', '*seen', 'vowel' states this.betaState = 'reset'

// key input characters contributing to the current output character this.inProgressInput = '' // Greek letter developed from the input we've seen so far this.inProgressGreek = '' // Greek upper/lowercased vowel or RHO before combination with any diacritics this.inProgressGreekBase = '' // beta code diacritics to combine with vowel or RHO this.inProgressDiacritics = '' }

/** * Reset state of Beta code input converter. * * Note that here the defaulted values of the config parameters simply * copy the previous state. */

// Externally called entry point

GreekBeta.prototype.reset = function (   isStrict,    preferGreekExtended,    preferPrecomposed ) { if (undefined !== isStrict          ) this.isStrict = isStrict if (undefined !== preferPrecomposed ) this.preferPrecomposed = preferPrecomposed if (undefined !== preferGreekExtended) this.preferGreekExtended = preferGreekExtended

this.resetState }

// Externally called entry point

GreekBeta.prototype.resetState = function { this.betaState = 'reset' this.inProgressInput = '' this.inProgressGreek = '' this.inProgressGreekBase = '' this.inProgressDiacritics = '' }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// Data supporting interactions with Beta Code key input and Greek

// Translate plain keyboard alphabetic key to base Greek letter

// All of the alphabetic keys A-Z a-z are used below. // Note the non-standard entry for 'J' and 'j' for FINAL SIGMA

var betaAlphaKeyToGreek = { "A": "\u0391",   //  Α "B": "\u0392",   //  Β "G": "\u0393",   //  Γ "D": "\u0394",   //  Δ "E": "\u0395",   //  Ε "V": "\u03DC",   //  Ϝ "Z": "\u0396",   //  Ζ "H": "\u0397",   //  Η "Q": "\u0398",   //  Θ "I": "\u0399",   //  Ι "K": "\u039A",   //  Κ "L": "\u039B",   //  Λ "M": "\u039C",   //  Μ "N": "\u039D",   //  Ν "C": "\u039E",   //  Ξ "O": "\u039F",   //  Ο "P": "\u03A0",   //  Π "R": "\u03A1",   //  Ρ "S": "\u03A3",   //  Σ      S, S1  "J":  "\u03C2",   //     ς   S, S2, J    (not sure about this usage) "T": "\u03A4",   //  Τ "U": "\u03A5",   //  Υ "F": "\u03A6",   //  Φ "X": "\u03A7",   //  Χ "Y": "\u03A8",   //  Ψ "W": "\u03A9",   //  Ω

"a": "\u03B1",   //  α "b": "\u03B2",   //  β "g": "\u03B3",   //  γ "d": "\u03B4",   //  δ "e": "\u03B5",   //  ε "v": "\u03DD",   //  ϝ "z": "\u03B6",   //  ζ "h": "\u03B7",   //  η "q": "\u03B8",   //  θ "i": "\u03B9",   //  ι "k": "\u03BA",   //  κ "l": "\u03BB",   //  λ "m": "\u03BC",   //  μ "n": "\u03BD",   //  ν "c": "\u03BE",   //  ξ "o": "\u03BF",   //  ο "p": "\u03C0",   //  π "r": "\u03C1",   //  ρ "s": "\u03C3",   //  σ    S, S1  "j":  "\u03C2",   //  ς    S, S2, J  "t":  "\u03C4",   //  τ "u": "\u03C5",   //  υ "f": "\u03C6",   //  φ "x": "\u03C7",   //  χ "y": "\u03C8",   //  ψ "w": "\u03C9",   //  ω }

// Classify keyboard characters

function isLatinAlpha(key) { return (key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z') }

var betaAlphaVowelKeysInclude = 'AEHIOUWaehiouw' // ΑΕΗΙΟΥΩαεηιουω

var betaAlphaConsonantKeysInclude = 'BCDFGJKLMNPQRSTVXYZbcdfgjklmnpqrstvxyz' // ΒΞΔΦΓςΚΛΜΝΠΘΡΣΤϜΧΨΖβξδφγςκλμνπθρστϝχψζ

var betaAlphaDiacriticKeysInclude = ")(/=\\+|&'"

function isBetaVowelKey(key) { return betaAlphaVowelKeysInclude.includes(key) }

function isBetaConsonantKey(key) { return betaAlphaConsonantKeysInclude.includes(key) }

function isBetaDiacriticKey(key) { return betaAlphaDiacriticKeysInclude.includes(key) }

//var betaAlphaPunctKeysInclude = " .,:;'-—#"

var betaPunctKeyOutputChars = { ":": "\u00B7",   // · Colon aka Greek semicolon      (0387 -> 00B7) //     aka MIDDOT aka Interpunct //     aka "Ano Teleia" aka "Ano Stigme"

"'": "\u2019",   // ’ Apostrophe           ' -> ’ 2019  RIGHT SINGLE QUOTATION MARK  &rsquo;

"_": "\u2014",   //  Dash from underline   _ -> — 2014  EM DASH           &mdash;

"#": "\u02B9",   //  Greek Numeral sign (Keraia)  # ʹ   0023 -> 02B0

// Others as yet are not translated // ";":  ";",        //  Greek Question Mark   ; -> ; 003B           (037E -> 003B) // " ":  " ",        //  Space // ".":  ".",        //  Period. 002E // ",":  ",",        //  Comma              , 002C // "-":  "-",        //  Hyphen              ‐ 2010    - 002D //                   //                            02B9 MODIFIER LETTER PRIME //                   //                            0374 GREEK NUMERAL SIGN }

var betaAlphaPunctKeysInclude = Object.keys(betaPunctKeyOutputChars).join('') //console.log(` betaAlphaPunctKeysInclude:  "${betaAlphaPunctKeysInclude}"`)

function isBetaPunctKey(key) { return betaAlphaPunctKeysInclude.includes(key) }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

//=====================================================================

// Beta code has so many odd input combinations that parsing is difficult. // // 'S' might actually be 'S3'. 'R' might be followed by breathing marks, // but one combination ("*r)") is invalid. Uppercase forced by leading // '*'. Some combinations of diacritics on vowels is complex, and some // combinations aren't possible. The end of a vowel plus diacritics is // often only detected when a following key isn't a valid diacritic. // // And the environment we cater to is not your usual parsing setup. We // must process user input key by key. //

//XXX Consider action functions for each of these common stanzas to //      reduce the duplication of lines //XXX Combine clauses that accomplish the same actions

/** * @param {string} keyInput - the next input key characters * @returns {NextKeyResult} * * @typedef {object} NextKeyResult * @property {string} output - result of processing input chars through Beta code conversions * @property {string} input - accumulated key input chars that contributed to output result * @property {string} pending - latest key input char, when not yet processed by conversion; * @property {string} [greek] - intermediate Greek char value */ //XXX Add greek ? ; ensure updated everywhere we have an intermediate char value //   'input' is the keyboard keys contributing - 'greek' is the construction so far // No output ; another input key is needed ; previous input is input // result = { input: this.inProgressInput } // No output ; another input key is needed ; previous input is input ; //     intermediate Greek character is greek // result = { input: this.inProgressInput, greek: this.inProgressGreek} // result = { input: this.inProgressInput, greek: this.inProgressGreek} // result = { input: this.inProgressInput, greek: this.inProgressGreek} // result = { input: this.inProgressInput, greek: this.inProgressGreek} // result = { input: this.inProgressInput, greek: this.inProgressGreek} // result = { input: this.inProgressInput, greek: this.inProgressGreek} // result = { input: this.inProgressInput, greek: this.inProgressGreek} // Output character result ; previous input is input // result = { output, input: nextKey } // result = { output, input: nextKey } // result = { output, input: this.inProgressInput} // Output character result ; call again with 'pending' input key ; previous input is input // result = { output, input: this.inProgressInput, pending: nextKey} // result = { output, input: this.inProgressInput, pending: nextKey} // result = { output, input: this.inProgressInput, pending: nextKey} // result = { output, input: this.inProgressInput, pending: nextKey}

// The internal states needed are the result of the occasions when we must // wait to check the next following input key character to know when a // letter is finished and/or which letter should be the output result. // They represent the memory of previous input characters and the fact that // conversion is "in progress". // // reset - initial state where input begins new Greek letter. // // *Seen - an input asterisk signals that the following Greek consonant or // vowel letter should be capitalized. // // rSeen - the letter RHO might be followed by a breathing mark. // // sSeen - the letter SIGMA might be followed by a digit altering which // SIGMA - SIGMA, MEDIAL SIGMA, FINAL SIGMA, LUNATE SIGMA - to output. // // vowel - a vowel letter has begun and we are looking for any following // diacritic input characters. Multiple diacritic input characters can be // accumulated to generate the final result desired.

// Externally called entry point

GreekBeta.prototype.processNextInputKey = function (keyInput) { // The next input key to process under the current state var nextKey = keyInput var result = null console.log(' processNextInputKey:  keyInput "'+keyInput+'"   state "'+this.betaState+'"   greek "'+this.inProgressGreek+'"   input "'+this.inProgressInput+'"')

// Parsers usually have the capability to 'lookahead' into an input // stream, using the next input char to signal when the current state // is completed. // Upon call with an input character we might be able to immediately // return an output character. // //  // Here instead we may have request the next key input character from // the caller // Here we may have to // complete the current state when we check the next input char, // but not *yet* process the next input char. // We loop back to the top here to process the char after some // state transitions. //

console.log(' processNextInputKey:     nextKey "'+nextKey+'"   state "'+this.betaState+'"') var output

if (this.betaState === 'reset') { if (nextKey === '*') { this.inProgressInput = nextKey this.betaState = '*Seen' result = { input: this.inProgressInput } // reset & *     ->*Seen   consumed,   save key

} else if (nextKey === 'R' || nextKey === 'r') { this.inProgressInput = nextKey if (this.isStrict) { this.inProgressGreek = betaAlphaKeyToGreek[nextKey.toLowerCase] } else { this.inProgressGreek = betaAlphaKeyToGreek[nextKey] }     this.betaState = 'rSeen' result = { input: this.inProgressInput, greek: this.inProgressGreek} // reset & Rr    ->rSeen   consumed,   save lookup key,    set greek

} else if (nextKey === 'S' || nextKey === 's') { this.inProgressInput = nextKey if (this.isStrict) { this.inProgressGreek = betaAlphaKeyToGreek[nextKey.toLowerCase] } else { this.inProgressGreek = betaAlphaKeyToGreek[nextKey] }     this.betaState = 'sSeen' result = { input: this.inProgressInput, greek: this.inProgressGreek} // reset & Ss    ->sSeen   consumed,   save lookup key,    set greek

} else if (isBetaConsonantKey(nextKey)) { if (this.isStrict) { output = betaAlphaKeyToGreek[nextKey.toLowerCase] } else { output = betaAlphaKeyToGreek[nextKey] }     console.log('  processNextInputKey:      output "'+output+'"') result = { output: output, input: nextKey } // reset & K     ->reset   consumed,   output lookup key

} else if (isBetaVowelKey(nextKey)) { this.inProgressInput = nextKey if (this.isStrict) { this.inProgressGreek = betaAlphaKeyToGreek[nextKey.toLowerCase] } else { this.inProgressGreek = betaAlphaKeyToGreek[nextKey] }     this.inProgressGreekBase = this.inProgressGreek this.inProgressDiacritics = '' this.betaState = 'vowel' result = { input: this.inProgressInput, greek: this.inProgressGreek} // reset & V     ->vowel   consumed,   save lookup key,    set greek/base

} else { output = nextKey if (nextKey in betaPunctKeyOutputChars) { output = betaPunctKeyOutputChars[nextKey] }     console.log('  processNextInputKey:      output "'+output+'"') result = { output: output, input: nextKey } // reset & P     ->reset   consumed,   output lookup key, // reset & O     ->reset   consumed,   output key,

}

// State "RHO has been seen" - do we have a following breathing mark? } else if (this.betaState === 'rSeen') { // " Ρ "  "GREEK CAPITAL LETTER RHO"             "\u03A1" // " ρ "  "GREEK SMALL LETTER RHO"               "\u03C1" // " ῤ "  "GREEK SMALL LETTER RHO WITH PSILI"    "\u1FE4"   "\u03C1\u0313"   GREEK SMALL LETTER RHO    COMBINING COMMA ABOVE // " ῥ "  "GREEK SMALL LETTER RHO WITH DASIA"    "\u1FE5"   "\u03C1\u0314"   GREEK SMALL LETTER RHO    COMBINING REVERSED COMMA ABOVE // " Ῥ "  "GREEK CAPITAL LETTER RHO WITH DASIA"  "\u1FEC"   "\u03A1\u0314"   GREEK CAPITAL LETTER RHO  COMBINING REVERSED COMMA ABOVE //   // RHO may be followed by a breathing mark, but there isn't a valid // combination of uppercase RHO with PSILI "*r)" . Rather than having   // an explicit error message or throw, we'll default to emitting the    // CAPITAL RHO and then the ')'.

output = this.inProgressGreek if (nextKey === '(') {     this.inProgressInput += nextKey      // DASIA can combine with either uppercase or lowercase RHO      if (output === '\u03A1') {        output = "\u1FEC"      } else {        output = "\u1FE5"      }      nextKey = null //  rSeen & (     ->reset   consumed,   output updated greek,   null input/greek

} else if (nextKey === ')' &&  this.inProgressGreek === '\u03C1') {      this.inProgressInput += nextKey      // lowercase RHO can have PSILI      output = "\u1FE4"      nextKey = null //  rSeen & )     ->reset   consumed,   output updated greek,   null input/greek

} else { // rSeen & ??? ->reset  unconsumed, output greek,           null input/greek //     * K R S P O diacritics }

if (!this.preferPrecomposed &&  output in greekCharsWithNFD) { output = greekCharsWithNFD[output] }   console.log('  processNextInputKey:      output "'+output+'"') result = { output: output, input: this.inProgressInput, pending: nextKey} this.inProgressInput = null this.inProgressGreek = null this.betaState = 'reset'

// State "S has been seen" - what modifiers might follow? } else if (this.betaState === 'sSeen') { // These inputs  *S  *S3  S  S1  S2  S3   result in these chars: // " Σ "   "GREEK CAPITAL LETTER SIGMA"               "\u03A3"  *S // " σ "   "GREEK SMALL LETTER SIGMA"                 "\u03C3"   S  S1   (MEDIAL) // " ς "   "GREEK SMALL LETTER FINAL SIGMA"           "\u03C2"   S2    //  " Ϲ "   "GREEK CAPITAL LUNATE SIGMA SYMBOL"        "\u03F9"  *S3 // " ϲ "   "GREEK LUNATE SIGMA SYMBOL"                "\u03F2"   S3    // this.inProgressGreek is either 'Σ' or 'σ' output = this.inProgressGreek if (nextKey === '1') { this.inProgressInput += nextKey // preserve output as is     nextKey = null // sSeen & 1     ->reset   consumed    output greek            null input/greek //XXX Wait, what if I enter "s1" ? shouldn't that be forced to uppercase?

} else if (nextKey === '2') { this.inProgressInput += nextKey output = 'ς' nextKey = null // sSeen & 2     ->reset   consumed    output updated greek    null input/greek

} else if (nextKey === '3') { this.inProgressInput += nextKey if (this.inProgressGreek === 'Σ') { output = 'Ϲ' } else { output = 'ϲ' }     nextKey = null // sSeen & 3     ->reset   consumed    output updated greek    null input/greek

} else if (isBetaConsonantKey(nextKey) ||               isBetaVowelKey(nextKey) ) { // appears the word is being continued, so not final SIGMA // preserve output as is // sSeen & K     ->reset   unconsumed  output greek            null input/greek // sSeen & V     ->reset   unconsumed  output greek            null input/greek

} else { // appears the word is being ended with punctuation or something // we don't recognize, so check ...     // Is this a MEDIAL SIGMA that could be a FINAL SIGMA? if (this.inProgressGreek === 'σ') { output = 'ς' }   }

console.log(' processNextInputKey:      output "'+output+'"') result = { output: output, input: this.inProgressInput, pending: nextKey} this.inProgressInput = null this.inProgressGreek = null this.betaState = 'reset' // sSeen & ??? ->reset  unconsumed  output greek            null input/greek

// State "* has been seen" - does a letter follow? } else if (this.betaState === '*Seen') { if (nextKey === 'R' || nextKey === 'r') { this.inProgressInput += nextKey this.inProgressGreek = betaAlphaKeyToGreek[nextKey.toUpperCase] this.betaState = 'rSeen' result = { input: this.inProgressInput, greek: this.inProgressGreek} // *Seen & Rr    ->rSeen   consumed    save key                set updated greek

} else if (nextKey === 'S' || nextKey === 's') { this.inProgressInput += nextKey this.inProgressGreek = betaAlphaKeyToGreek[nextKey.toUpperCase] this.betaState = 'sSeen' result = { input: this.inProgressInput, greek: this.inProgressGreek} // *Seen & Ss    ->sSeen   consumed    save key                set updated greek

} else if (isBetaConsonantKey(nextKey)) { this.inProgressInput += nextKey // force output of uppercased letter output = betaAlphaKeyToGreek[nextKey.toUpperCase] console.log(' processNextInputKey:      output "'+output+'"') result = { output:output, input: this.inProgressInput} this.inProgressInput = null this.betaState = 'reset' // *Seen & K     ->reset   consumed    output updated greek    null input

} else if (isBetaVowelKey(nextKey)) { this.inProgressInput += nextKey this.inProgressGreek = betaAlphaKeyToGreek[nextKey.toUpperCase] this.inProgressGreekBase = this.inProgressGreek this.inProgressDiacritics = '' this.betaState = 'vowel' result = { input: this.inProgressInput, greek: this.inProgressGreek} // *Seen & V     ->vowel   consumed    save key                set greek/base

} else { // Either another '*', punctuation or something unrecognized, // so output the previously seen '*', and loop output = this.inProgressInput console.log(' processNextInputKey:      output "'+output+'"') result = { output: output, input: this.inProgressInput, pending: nextKey} this.inProgressInput = null this.betaState = 'reset' // *Seen & ??? ->reset  unconsumed  output input            null input }

// State "vowel is being processed" - do (more) diacritics follow? } else if (this.betaState === 'vowel') {

// Add this diacritic into continuing vowel composition if (isBetaDiacriticKey(nextKey)) { // We don't want to update progress state until we know that // the added diacritic creates a valid composition var inProgressInput = this.inProgressInput + nextKey var inProgressDiacritics = this.inProgressDiacritics + nextKey console.log('   inProgressInput      "'+inProgressInput+'"') console.log('   inProgressGreekBase  "'+this.inProgressGreekBase+'"') console.log('   inProgressDiacritics "'+inProgressDiacritics+'"')

// Generate a complete character composition from the Greek base // letter plus the current combiners.

// Ensure diacritics are in correct ordering for Unicode var temp1 = this.reorderBetaCodeDiacritics(                   inProgressDiacritics, this.isStrict) console.log('   reordered diacritics "'+temp1+'"') // Then convert to Unicode combiner characters var temp2 = betaCodeDiacriticsToCombiners(temp1) console.log('   as combiners "'+codePointsAsUnicodeEscapes(temp2)+'"')

var probeComposition = this.inProgressGreekBase + temp2 console.log('   base + combiners "'+codePointsAsUnicodeEscapes(probeComposition)+'"')

// it is possible for more than one char to match, because of the // duplications between 0370-3FF and 1F00-1FFF Unicode ranges var matchedKeys = Object.keys(greekCharsWithNFD).filter(function (key) {       if (probeComposition === greekCharsWithNFD[key]) {          console.log('      found composition "'+key+'" : '+codePointsAsUnicodeEscapes(key)+'')          return true        }      }) console.log('   found matchedKeys', matchedKeys)

if (matchedKeys.length) { var matchedKey console.log('     preferGreekExtended  '+this.preferGreekExtended+'') console.log('     matchedKeys.length   '+matchedKeys.length+'') if (this.preferGreekExtended &&  matchedKeys.length === 2) { matchedKey = matchedKeys[1] } else { matchedKey = matchedKeys[0] }       console.log('      found matched key "'+matchedKey+'" : '+codePointsAsUnicodeEscapes(matchedKey)+'') if (this.preferPrecomposed) { this.inProgressGreek = matchedKey } else { this.inProgressGreek = greekCharsWithNFD[matchedKey] }

//XXX Can we (should we) detect when the latest diacritic means the vowel //   is ended? For instance, a MACRON means we are done. An IOTA should //   mean we are done? //   Or do we leave it up to the next key to decide, as the caller might //   allow users to backspace and edit diacritics in progress?

} else { //XXX The latest diacritic combination does not match any //   valid known Greek letter composition. throw new Error('processNextInputKey: no known Greek letter composition for diacritics "'+inProgressDiacritics+'" and base letter "'+this.inProgressGreekBase+'"') //XXX yes the added diacritic is 'bad', but how to react? //   We could throw an error //   Or we could output the Greek letter as constructed so far, then //   output the faulty diacritic character, just like we would for //   punctuation or other trash, or when vowel was 'complete' //XXX We'd like to allow caller to 'flash' an error to user, with or without //   adding the bad character to buffer }     this.inProgressInput = inProgressInput this.inProgressDiacritics = inProgressDiacritics this.betaState = 'vowel' result = { input: this.inProgressInput, greek: this.inProgressGreek} // vowel & diac  ->vowel   consumed                            set updated greek

} else { // vowel is complete output = this.inProgressGreek console.log(' processNextInputKey:      output "'+output+'"') result = { output: output, input: this.inProgressInput, pending: nextKey} this.inProgressInput = '' this.inProgressGreek = '' this.inProgressGreekBase = '' this.inProgressDiacritics = '' this.betaState = 'reset' // vowel & ??? ->reset  unconsumed  output greek            null input/greek }

} else { throw new Error('processNextInputKey: bad state "'+this.betaState+'" found') }

console.log(' processNextInputKey:  end state "'+this.betaState+'"') console.log('       inProgressInput      "'+this.inProgressInput+'"') console.log('       inProgressGreek      "'+this.inProgressGreek+'"') console.log('       inProgressGreekBase  "'+this.inProgressGreekBase+'"') console.log('       inProgressDiacritics "'+this.inProgressDiacritics+'"') console.log('     returned result', result) //XXX check that result has ? //   output //   input  and  pending return result }

//** Strict - also reflecting ordering of combiners in Unicode decompositions // *However*, we shouldn't rely on this in case we change processing // and diacritics and combiners diverge; //   * have one table for ordering diacritic beta code chars //       and of course handle strict vs. loose differences //   * another table for ordering Unicode combiners //   * and then tables to convert back and forth between chars and combiners //   * functions for all these

var betaCodeDiacriticsToCombiners_ = { ')': '\u0313',   // 1, PSILI         COMBINING COMMA ABOVE               Smooth breathing  '(':  '\u0314',   // 1, DASIA         COMBINING REVERSED COMMA ABOVE      Rough breathing '+': '\u0308',   // 1, DIALYTIKA     COMBINING DIAERESIS                 Dialytika '/': '\u0301',   // 2, OXIA          COMBINING ACUTE ACCENT              Acute accent '=': '\u0342',   // 2, PERISPOMENI   COMBINING GREEK PERISPOMENI         Perispomeni '\\': '\u0300',  // 2, VARIA         COMBINING GRAVE ACCENT              Grave accent '|': '\u0345',   // 3, YPOGEGRAMMENI COMBINING GREEK YPOGEGRAMMENI       Ypogegrammeni '&': '\u0304',   // 5, MACRON        COMBINING MACRON                    Macron "'": '\u0306',   // 5, BREVE         COMBINING BREVE                     Breve }

var combinersToBetaCodeDiacritics_ = swapKeysValues(betaCodeDiacriticsToCombiners_)

function betaCodeDiacriticsToCombiners(str) { var result = '' var char

var chars = str.split('') for (var idx=0; idx < chars.length; idx++) { var char = chars[idx] //console.log(' betaCodeDiacriticsToCombiners: char "'+char+'"') if (char in betaCodeDiacriticsToCombiners_) { result += betaCodeDiacriticsToCombiners_[char] } else { console.error(' betaCodeDiacriticsToCombiners: char "'+char+'" is not a known diacritic') //XXX should be Error ? } }

return result }

function combinersToBetaCodeDiacritics(str) { var result = ''

var chars = str.split('') for (var idx=0; idx < chars.length; idx++) { var char = chars[idx] if (char in combinersToBetaCodeDiacritics_) { result += combinersToBetaCodeDiacritics_[char] } else { console.error(' combinersToBetaCodeDiacritics_: char "'+char+'" is not a known diacritic combiner') } }

return result }

// We have observed the following preferred ordering of our combining // characters when looking at Unicode's NKD decompositions. If we are // to output Greek letters as combinations, then generating output in // the same ordering hopefully is 'better'. // // Note that this is only a subset of all Unicode combiners, only the // ones we need to generate our composed Greek letters.

var betaCodeCombinersOrdering_ = { '\u0314': 1, // DASIA         COMBINING REVERSED COMMA ABOVE  Rough breathing '\u0308': 1, // DIALYTIKA     COMBINING DIAERESIS             Dialytika '\u0313': 1, // PSILI         COMBINING COMMA ABOVE           Smooth breathing '\u0301': 2, // OXIA          COMBINING ACUTE ACCENT          Acute accent '\u0342': 2, // PERISPOMENI   COMBINING GREEK PERISPOMENI     Perispomeni '\u0300': 2, // VARIA         COMBINING GRAVE ACCENT          Grave accent '\u0345': 3, // YPOGEGRAMMENI COMBINING GREEK YPOGEGRAMMENI   Ypogegrammeni '\u0306': 5, // BREVE         COMBINING BREVE                 Breve '\u0304': 5, // MACRON        COMBINING MACRON                Macron }

/** * Return string of Greek combiners in Unicode preferred order. * * @param {string} str - string of Unicode combiner characters. * @returns {string} - combiners reordered into Unicode preferred order. */

GreekBeta.prototype.reorderGreekCombiners = function (str) { //var chars = Array.from(str) //XXX ES5 port - I think Array.from is ES6 and up var chars = str.split('') chars = chars.sort(function (a,b) {   var aPriority = betaCodeCombinersOrdering_[a]    var bPriority = betaCodeCombinersOrdering_[b]    return  aPriority - bPriority  }) return chars.join('') }

// Under strict Beta Code the order of diacritic characters matters, // where the breathing marks must come first when present, then // accents, etc. Here we specify diacritic 'classes' with numbers // in correct strict order. // // Note that diacritics in the same class are exclusive of each other; // e.g. you can't have PSILI and DIALYTIKA together

var betaCodeDiacriticsOrdering_ = { ')': 1,    // PSILI         COMBINING COMMA ABOVE               Smooth breathing  '(':  1,    // DASIA         COMBINING REVERSED COMMA ABOVE      Rough breathing '+': 1,    // DIALYTIKA     COMBINING DIAERESIS                 Dialytika '/': 2,    // OXIA          COMBINING ACUTE ACCENT              Acute accent '=': 2,    // PERISPOMENI   COMBINING GREEK PERISPOMENI         Perispomeni '\\': 2,   // VARIA         COMBINING GRAVE ACCENT              Grave accent '|': 3,    // YPOGEGRAMMENI COMBINING GREEK YPOGEGRAMMENI       Ypogegrammeni '&': 5,    // MACRON        COMBINING MACRON                    Macron "'": 5,    // BREVE         COMBINING BREVE                     Breve }

/** * Return string of beta code diacritics in strict order. * * @param {string} str - string of beta code diacritics * @returns {string} - diacritics reordered into strict order */

GreekBeta.prototype.reorderBetaCodeDiacriticsPermissive = function (str) { var chars = str.split('') chars = chars.sort(function (a,b) {   var aPriority = betaCodeDiacriticsOrdering_[a]    var bPriority = betaCodeDiacriticsOrdering_[b]    return  aPriority - bPriority  }) return chars.join('') }

/** * Check string of beta code diacritic characters for problems. * * Characters must be valid beta code diacritic characters. * A macron or breve diacritic must be found alone, not in combination * with any other diacritic. * Diacritics of the same class can only appear once, so ')+' will fail. * If parameter strict is true, then diacritics must be found in strict * beta code order - input '|/(' will fail while '(/|' will succeed. * * Note that '&/' and '/&' both correctly fail, but with different messages. * * @param {string} str - string of beta code diacritics * @param {boolean} [strict=false] - whether diacritics must be in strict order * @returns {string} - diacritics reordered into strict order * @throws {Error} if bad order or duplicate priority diacritics found */

GreekBeta.prototype.reorderBetaCodeDiacritics = function (str, isStrict) { if (undefined === isStrict) isStrict = false // remember the diacritics we see in their priority order. var seenDiacritics = [] var latestPriority = 0

var chars = str.split('') for (var idx=0; idx < chars.length; idx++) { var char = chars[idx] //console.log(' reorderBetaCodeDiacritics: char "'+char+'"') if (char in betaCodeDiacriticsOrdering_) { var priority = betaCodeDiacriticsOrdering_[char] if (priority < latestPriority &&  isStrict) { throw new Error('reorderBetaCodeDiacritics: beta code diacritic "'+char+'" is out of order in "'+str+'"') }     latestPriority = priority

// if another diacritic of the same class is seen, complain. if (seenDiacritics[priority]) { throw new Error('reorderBetaCodeDiacritics: beta code diacritic "'+char+'" invalid with "'+seenDiacritics.join('')+'"') }

// remember seen diacritics in priority order seenDiacritics[priority] = char

} else { throw new Error('reorderBetaCodeDiacritics: "'+char+'" is invalid beta code diacritic in "'+str+'"') } }

var result = seenDiacritics.join('')

// MACRON and BREVE are exclusive of any other diacritics (and each other) if (seenDiacritics[5] &&  result.length > 1) { throw new Error('reorderBetaCodeDiacritics: beta code diacritic "'+seenDiacritics[5]+'" must be only diacritic, seen with "'+result+'"') }

return result }

// Looking at Unicode NFD decompositions of Greek letters we have // seen these combinations and orderings of combiners: // //      \u0300    VARIA       COMBINING GRAVE ACCENT //     \u0301    OXIA        COMBINING ACUTE ACCENT //     \u0304    MACRON      COMBINING MACRON //     \u0306    BREVE       COMBINING BREVE //     \u0308    DIAERESIS   COMBINING DIAERESIS //     \u0313    PSILI       COMBINING COMMA ABOVE             smooth  ")", "\u02BC", "\u1FBF", //      \u0314    DASIA       COMBINING REVERSED COMMA ABOVE    rough //      \u0342                COMBINING GREEK PERISPOMENI       tilde //      \u0345                COMBINING GREEK YPOGEGRAMMENI     iota below //  //              \u0300  \u0345               VARIA   YPOGEGRAMMENI //              \u0301  \u0345               OXIA    YPOGEGRAMMENI //      \u0308  \u0300            DIAERESIS  VARIA //      \u0308  \u0301            DIAERESIS  OXIA //      \u0308  \u0342            DIAERESIS  tilde //      \u0313  \u0300              PSILI    VARIA //      \u0313  \u0301              PSILI    OXIA //      \u0313  \u0342              PSILI    tilde //      \u0313          \u0345      PSILI            YPOGEGRAMMENI //      \u0314  \u0300              DASIA    VARIA //      \u0314  \u0301              DASIA    OXIA //     \u0314  \u0342              DASIA    tilde //     \u0314          \u0345      DASIA            YPOGEGRAMMENI //             \u0342  \u0345               tilde   YPOGEGRAMMENI // //      \u0313  \u0300  \u0345      PSILI    VARIA   YPOGEGRAMMENI //     \u0313  \u0301  \u0345      PSILI    OXIA    YPOGEGRAMMENI //     \u0313  \u0342  \u0345      PSILI    tilde   YPOGEGRAMMENI //     \u0314  \u0300  \u0345      DASIA    VARIA   YPOGEGRAMMENI //     \u0314  \u0301  \u0345      DASIA    OXIA    YPOGEGRAMMENI //     \u0314  \u0342  \u0345      DASIA    tilde   YPOGEGRAMMENI

// These are the known decompositions of all Greek precomposed characters. // Only those characters that can be replaced are found below. // Most are replaceable by a base Greek letter followed by combiners. // A few can be replaced by another single Unicode character.

var greekCharsWithNFD = { // Unicode   NFD codepoints                  char    Unicode name                                             NFD Unicode names "\u0340":  "\u0300",                   //  " ̀ "  "COMBINING GRAVE TONE MARK"                                COMBINING GRAVE ACCENT "\u0341":  "\u0301",                   //  " ́ "  "COMBINING ACUTE TONE MARK"                                COMBINING ACUTE ACCENT "\u0343":  "\u0313",                   //  " ̓ "  "COMBINING GREEK KORONIS"                                  COMBINING COMMA ABOVE "\u0344":  "\u0308\u0301",             //  " ̈́ "  "COMBINING GREEK DIALYTIKA TONOS"                          COMBINING DIAERESIS   COMBINING ACUTE ACCENT

"\u0374":  "\u02B9",                   //  " ʹ "  "GREEK NUMERAL SIGN"                                      MODIFIER LETTER PRIME "\u037E":  "\u003B",                   //  " ; "  "GREEK QUESTION MARK"                                     SEMICOLON "\u0385":  "\u00A8\u0301",             //  " ΅ "  "GREEK DIALYTIKA TONOS"                                   DIAERESIS   COMBINING ACUTE ACCENT "\u0386":  "\u0391\u0301",             //  " Ά "  "GREEK CAPITAL LETTER ALPHA WITH TONOS"                   GREEK CAPITAL LETTER ALPHA      COMBINING ACUTE ACCENT "\u0387":  "\u00B7",                   //  " · "  "GREEK ANO TELEIA"                                        MIDDLE DOT "\u0388":  "\u0395\u0301",             //  " Έ "  "GREEK CAPITAL LETTER EPSILON WITH TONOS"                 GREEK CAPITAL LETTER EPSILON    COMBINING ACUTE ACCENT "\u0389":  "\u0397\u0301",             //  " Ή "  "GREEK CAPITAL LETTER ETA WITH TONOS"                     GREEK CAPITAL LETTER ETA        COMBINING ACUTE ACCENT "\u038A":  "\u0399\u0301",             //  " Ί "  "GREEK CAPITAL LETTER IOTA WITH TONOS"                    GREEK CAPITAL LETTER IOTA       COMBINING ACUTE ACCENT "\u038C":  "\u039F\u0301",             //  " Ό "  "GREEK CAPITAL LETTER OMICRON WITH TONOS"                 GREEK CAPITAL LETTER OMICRON    COMBINING ACUTE ACCENT "\u038E":  "\u03A5\u0301",             //  " Ύ "  "GREEK CAPITAL LETTER UPSILON WITH TONOS"                 GREEK CAPITAL LETTER UPSILON    COMBINING ACUTE ACCENT "\u038F":  "\u03A9\u0301",             //  " Ώ "  "GREEK CAPITAL LETTER OMEGA WITH TONOS"                   GREEK CAPITAL LETTER OMEGA      COMBINING ACUTE ACCENT "\u0390":  "\u03B9\u0308\u0301",       //  " ΐ "  "GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS"        GREEK SMALL LETTER IOTA         COMBINING DIAERESIS   COMBINING ACUTE ACCENT "\u03AA":  "\u0399\u0308",             //  " Ϊ "  "GREEK CAPITAL LETTER IOTA WITH DIALYTIKA"                GREEK CAPITAL LETTER IOTA       COMBINING DIAERESIS "\u03AB":  "\u03A5\u0308",             //  " Ϋ "  "GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA"             GREEK CAPITAL LETTER UPSILON    COMBINING DIAERESIS "\u03AC":  "\u03B1\u0301",             //  " ά "  "GREEK SMALL LETTER ALPHA WITH TONOS"                     GREEK SMALL LETTER ALPHA        COMBINING ACUTE ACCENT "\u03AD":  "\u03B5\u0301",             //  " έ "  "GREEK SMALL LETTER EPSILON WITH TONOS"                   GREEK SMALL LETTER EPSILON      COMBINING ACUTE ACCENT "\u03AE":  "\u03B7\u0301",             //  " ή "  "GREEK SMALL LETTER ETA WITH TONOS"                       GREEK SMALL LETTER ETA          COMBINING ACUTE ACCENT "\u03AF":  "\u03B9\u0301",             //  " ί "  "GREEK SMALL LETTER IOTA WITH TONOS"                      GREEK SMALL LETTER IOTA         COMBINING ACUTE ACCENT "\u03B0":  "\u03C5\u0308\u0301",       //  " ΰ "  "GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS"     GREEK SMALL LETTER UPSILON      COMBINING DIAERESIS   COMBINING ACUTE ACCENT "\u03CA":  "\u03B9\u0308",             //  " ϊ "  "GREEK SMALL LETTER IOTA WITH DIALYTIKA"                  GREEK SMALL LETTER IOTA         COMBINING DIAERESIS "\u03CB":  "\u03C5\u0308",             //  " ϋ "  "GREEK SMALL LETTER UPSILON WITH DIALYTIKA"               GREEK SMALL LETTER UPSILON      COMBINING DIAERESIS "\u03CC":  "\u03BF\u0301",             //  " ό "  "GREEK SMALL LETTER OMICRON WITH TONOS"                   GREEK SMALL LETTER OMICRON      COMBINING ACUTE ACCENT "\u03CD":  "\u03C5\u0301",             //  " ύ "  "GREEK SMALL LETTER UPSILON WITH TONOS"                   GREEK SMALL LETTER UPSILON      COMBINING ACUTE ACCENT "\u03CE":  "\u03C9\u0301",             //  " ώ "  "GREEK SMALL LETTER OMEGA WITH TONOS"                     GREEK SMALL LETTER OMEGA        COMBINING ACUTE ACCENT "\u03D3":  "\u03D2\u0301",             //  " ϓ "  "GREEK UPSILON WITH ACUTE AND HOOK SYMBOL"                GREEK UPSILON WITH HOOK SYMBOL  COMBINING ACUTE ACCENT "\u03D4":  "\u03D2\u0308",             //  " ϔ "  "GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL"            GREEK UPSILON WITH HOOK SYMBOL  COMBINING DIAERESIS

// Unicode   NFD codepoints                char    Unicode name                                                                 NFD Unicode names "\u1F00":  "\u03B1\u0313",             //  " ἀ "  "GREEK SMALL LETTER ALPHA WITH PSILI"                                       GREEK SMALL LETTER ALPHA  COMBINING COMMA ABOVE "\u1F01":  "\u03B1\u0314",             //  " ἁ "  "GREEK SMALL LETTER ALPHA WITH DASIA"                                       GREEK SMALL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE "\u1F02":  "\u03B1\u0313\u0300",       //  " ἂ "  "GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA"                             GREEK SMALL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F03":  "\u03B1\u0314\u0300",       //  " ἃ "  "GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA"                             GREEK SMALL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F04":  "\u03B1\u0313\u0301",       //  " ἄ "  "GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA"                              GREEK SMALL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F05":  "\u03B1\u0314\u0301",       //  " ἅ "  "GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA"                              GREEK SMALL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F06":  "\u03B1\u0313\u0342",       //  " ἆ "  "GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI"                       GREEK SMALL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F07":  "\u03B1\u0314\u0342",       //  " ἇ "  "GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI"                       GREEK SMALL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F08":  "\u0391\u0313",             //  " Ἀ "  "GREEK CAPITAL LETTER ALPHA WITH PSILI"                                     GREEK CAPITAL LETTER ALPHA  COMBINING COMMA ABOVE "\u1F09":  "\u0391\u0314",             //  " Ἁ "  "GREEK CAPITAL LETTER ALPHA WITH DASIA"                                     GREEK CAPITAL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE "\u1F0A":  "\u0391\u0313\u0300",       //  " Ἂ "  "GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA"                           GREEK CAPITAL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F0B":  "\u0391\u0314\u0300",       //  " Ἃ "  "GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA"                           GREEK CAPITAL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F0C":  "\u0391\u0313\u0301",       //  " Ἄ "  "GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA"                            GREEK CAPITAL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F0D":  "\u0391\u0314\u0301",       //  " Ἅ "  "GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA"                            GREEK CAPITAL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F0E":  "\u0391\u0313\u0342",       //  " Ἆ "  "GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI"                     GREEK CAPITAL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F0F":  "\u0391\u0314\u0342",       //  " Ἇ "  "GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI"                     GREEK CAPITAL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F10":  "\u03B5\u0313",             //  " ἐ "  "GREEK SMALL LETTER EPSILON WITH PSILI"                                     GREEK SMALL LETTER EPSILON  COMBINING COMMA ABOVE "\u1F11":  "\u03B5\u0314",             //  " ἑ "  "GREEK SMALL LETTER EPSILON WITH DASIA"                                     GREEK SMALL LETTER EPSILON  COMBINING REVERSED COMMA ABOVE "\u1F12":  "\u03B5\u0313\u0300",       //  " ἒ "  "GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA"                           GREEK SMALL LETTER EPSILON  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F13":  "\u03B5\u0314\u0300",       //  " ἓ "  "GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA"                           GREEK SMALL LETTER EPSILON  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F14":  "\u03B5\u0313\u0301",       //  " ἔ "  "GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA"                            GREEK SMALL LETTER EPSILON  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F15":  "\u03B5\u0314\u0301",       //  " ἕ "  "GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA"                            GREEK SMALL LETTER EPSILON  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F18":  "\u0395\u0313",             //  " Ἐ "  "GREEK CAPITAL LETTER EPSILON WITH PSILI"                                   GREEK CAPITAL LETTER EPSILON  COMBINING COMMA ABOVE "\u1F19":  "\u0395\u0314",             //  " Ἑ "  "GREEK CAPITAL LETTER EPSILON WITH DASIA"                                   GREEK CAPITAL LETTER EPSILON  COMBINING REVERSED COMMA ABOVE "\u1F1A":  "\u0395\u0313\u0300",       //  " Ἒ "  "GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA"                         GREEK CAPITAL LETTER EPSILON  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F1B":  "\u0395\u0314\u0300",       //  " Ἓ "  "GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA"                         GREEK CAPITAL LETTER EPSILON  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F1C":  "\u0395\u0313\u0301",       //  " Ἔ "  "GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA"                          GREEK CAPITAL LETTER EPSILON  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F1D":  "\u0395\u0314\u0301",       //  " Ἕ "  "GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA"                          GREEK CAPITAL LETTER EPSILON  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F20":  "\u03B7\u0313",             //  " ἠ "  "GREEK SMALL LETTER ETA WITH PSILI"                                         GREEK SMALL LETTER ETA  COMBINING COMMA ABOVE "\u1F21":  "\u03B7\u0314",             //  " ἡ "  "GREEK SMALL LETTER ETA WITH DASIA"                                         GREEK SMALL LETTER ETA  COMBINING REVERSED COMMA ABOVE "\u1F22":  "\u03B7\u0313\u0300",       //  " ἢ "  "GREEK SMALL LETTER ETA WITH PSILI AND VARIA"                               GREEK SMALL LETTER ETA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F23":  "\u03B7\u0314\u0300",       //  " ἣ "  "GREEK SMALL LETTER ETA WITH DASIA AND VARIA"                               GREEK SMALL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F24":  "\u03B7\u0313\u0301",       //  " ἤ "  "GREEK SMALL LETTER ETA WITH PSILI AND OXIA"                                GREEK SMALL LETTER ETA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F25":  "\u03B7\u0314\u0301",       //  " ἥ "  "GREEK SMALL LETTER ETA WITH DASIA AND OXIA"                                GREEK SMALL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F26":  "\u03B7\u0313\u0342",       //  " ἦ "  "GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI"                         GREEK SMALL LETTER ETA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F27":  "\u03B7\u0314\u0342",       //  " ἧ "  "GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI"                         GREEK SMALL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F28":  "\u0397\u0313",             //  " Ἠ "  "GREEK CAPITAL LETTER ETA WITH PSILI"                                       GREEK CAPITAL LETTER ETA  COMBINING COMMA ABOVE "\u1F29":  "\u0397\u0314",             //  " Ἡ "  "GREEK CAPITAL LETTER ETA WITH DASIA"                                       GREEK CAPITAL LETTER ETA  COMBINING REVERSED COMMA ABOVE "\u1F2A":  "\u0397\u0313\u0300",       //  " Ἢ "  "GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA"                             GREEK CAPITAL LETTER ETA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F2B":  "\u0397\u0314\u0300",       //  " Ἣ "  "GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA"                             GREEK CAPITAL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F2C":  "\u0397\u0313\u0301",       //  " Ἤ "  "GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA"                              GREEK CAPITAL LETTER ETA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F2D":  "\u0397\u0314\u0301",       //  " Ἥ "  "GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA"                              GREEK CAPITAL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F2E":  "\u0397\u0313\u0342",       //  " Ἦ "  "GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI"                       GREEK CAPITAL LETTER ETA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F2F":  "\u0397\u0314\u0342",       //  " Ἧ "  "GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI"                       GREEK CAPITAL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F30":  "\u03B9\u0313",             //  " ἰ "  "GREEK SMALL LETTER IOTA WITH PSILI"                                        GREEK SMALL LETTER IOTA  COMBINING COMMA ABOVE "\u1F31":  "\u03B9\u0314",             //  " ἱ "  "GREEK SMALL LETTER IOTA WITH DASIA"                                        GREEK SMALL LETTER IOTA  COMBINING REVERSED COMMA ABOVE "\u1F32":  "\u03B9\u0313\u0300",       //  " ἲ "  "GREEK SMALL LETTER IOTA WITH PSILI AND VARIA"                              GREEK SMALL LETTER IOTA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F33":  "\u03B9\u0314\u0300",       //  " ἳ "  "GREEK SMALL LETTER IOTA WITH DASIA AND VARIA"                              GREEK SMALL LETTER IOTA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F34":  "\u03B9\u0313\u0301",       //  " ἴ "  "GREEK SMALL LETTER IOTA WITH PSILI AND OXIA"                               GREEK SMALL LETTER IOTA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F35":  "\u03B9\u0314\u0301",       //  " ἵ "  "GREEK SMALL LETTER IOTA WITH DASIA AND OXIA"                               GREEK SMALL LETTER IOTA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F36":  "\u03B9\u0313\u0342",       //  " ἶ "  "GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI"                        GREEK SMALL LETTER IOTA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F37":  "\u03B9\u0314\u0342",       //  " ἷ "  "GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI"                        GREEK SMALL LETTER IOTA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F38":  "\u0399\u0313",             //  " Ἰ "  "GREEK CAPITAL LETTER IOTA WITH PSILI"                                      GREEK CAPITAL LETTER IOTA  COMBINING COMMA ABOVE "\u1F39":  "\u0399\u0314",             //  " Ἱ "  "GREEK CAPITAL LETTER IOTA WITH DASIA"                                      GREEK CAPITAL LETTER IOTA  COMBINING REVERSED COMMA ABOVE "\u1F3A":  "\u0399\u0313\u0300",       //  " Ἲ "  "GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA"                            GREEK CAPITAL LETTER IOTA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F3B":  "\u0399\u0314\u0300",       //  " Ἳ "  "GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA"                            GREEK CAPITAL LETTER IOTA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F3C":  "\u0399\u0313\u0301",       //  " Ἴ "  "GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA"                             GREEK CAPITAL LETTER IOTA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F3D":  "\u0399\u0314\u0301",       //  " Ἵ "  "GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA"                             GREEK CAPITAL LETTER IOTA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F3E":  "\u0399\u0313\u0342",       //  " Ἶ "  "GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI"                      GREEK CAPITAL LETTER IOTA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F3F":  "\u0399\u0314\u0342",       //  " Ἷ "  "GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI"                      GREEK CAPITAL LETTER IOTA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F40":  "\u03BF\u0313",             //  " ὀ "  "GREEK SMALL LETTER OMICRON WITH PSILI"                                     GREEK SMALL LETTER OMICRON  COMBINING COMMA ABOVE "\u1F41":  "\u03BF\u0314",             //  " ὁ "  "GREEK SMALL LETTER OMICRON WITH DASIA"                                     GREEK SMALL LETTER OMICRON  COMBINING REVERSED COMMA ABOVE "\u1F42":  "\u03BF\u0313\u0300",       //  " ὂ "  "GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA"                           GREEK SMALL LETTER OMICRON  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F43":  "\u03BF\u0314\u0300",       //  " ὃ "  "GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA"                           GREEK SMALL LETTER OMICRON  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F44":  "\u03BF\u0313\u0301",       //  " ὄ "  "GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA"                            GREEK SMALL LETTER OMICRON  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F45":  "\u03BF\u0314\u0301",       //  " ὅ "  "GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA"                            GREEK SMALL LETTER OMICRON  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F48":  "\u039F\u0313",             //  " Ὀ "  "GREEK CAPITAL LETTER OMICRON WITH PSILI"                                   GREEK CAPITAL LETTER OMICRON  COMBINING COMMA ABOVE "\u1F49":  "\u039F\u0314",             //  " Ὁ "  "GREEK CAPITAL LETTER OMICRON WITH DASIA"                                   GREEK CAPITAL LETTER OMICRON  COMBINING REVERSED COMMA ABOVE "\u1F4A":  "\u039F\u0313\u0300",       //  " Ὂ "  "GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA"                         GREEK CAPITAL LETTER OMICRON  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F4B":  "\u039F\u0314\u0300",       //  " Ὃ "  "GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA"                         GREEK CAPITAL LETTER OMICRON  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F4C":  "\u039F\u0313\u0301",       //  " Ὄ "  "GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA"                          GREEK CAPITAL LETTER OMICRON  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F4D":  "\u039F\u0314\u0301",       //  " Ὅ "  "GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA"                          GREEK CAPITAL LETTER OMICRON  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F50":  "\u03C5\u0313",             //  " ὐ "  "GREEK SMALL LETTER UPSILON WITH PSILI"                                     GREEK SMALL LETTER UPSILON  COMBINING COMMA ABOVE "\u1F51":  "\u03C5\u0314",             //  " ὑ "  "GREEK SMALL LETTER UPSILON WITH DASIA"                                     GREEK SMALL LETTER UPSILON  COMBINING REVERSED COMMA ABOVE "\u1F52":  "\u03C5\u0313\u0300",       //  " ὒ "  "GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA"                           GREEK SMALL LETTER UPSILON  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F53":  "\u03C5\u0314\u0300",       //  " ὓ "  "GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA"                           GREEK SMALL LETTER UPSILON  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F54":  "\u03C5\u0313\u0301",       //  " ὔ "  "GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA"                            GREEK SMALL LETTER UPSILON  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F55":  "\u03C5\u0314\u0301",       //  " ὕ "  "GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA"                            GREEK SMALL LETTER UPSILON  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F56":  "\u03C5\u0313\u0342",       //  " ὖ "  "GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI"                     GREEK SMALL LETTER UPSILON  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F57":  "\u03C5\u0314\u0342",       //  " ὗ "  "GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI"                     GREEK SMALL LETTER UPSILON  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F59":  "\u03A5\u0314",             //  " Ὑ "  "GREEK CAPITAL LETTER UPSILON WITH DASIA"                                   GREEK CAPITAL LETTER UPSILON  COMBINING REVERSED COMMA ABOVE "\u1F5B":  "\u03A5\u0314\u0300",       //  " Ὓ "  "GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA"                         GREEK CAPITAL LETTER UPSILON  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F5D":  "\u03A5\u0314\u0301",       //  " Ὕ "  "GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA"                          GREEK CAPITAL LETTER UPSILON  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F5F":  "\u03A5\u0314\u0342",       //  " Ὗ "  "GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI"                   GREEK CAPITAL LETTER UPSILON  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F60":  "\u03C9\u0313",             //  " ὠ "  "GREEK SMALL LETTER OMEGA WITH PSILI"                                       GREEK SMALL LETTER OMEGA  COMBINING COMMA ABOVE "\u1F61":  "\u03C9\u0314",             //  " ὡ "  "GREEK SMALL LETTER OMEGA WITH DASIA"                                       GREEK SMALL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE "\u1F62":  "\u03C9\u0313\u0300",       //  " ὢ "  "GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA"                             GREEK SMALL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F63":  "\u03C9\u0314\u0300",       //  " ὣ "  "GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA"                             GREEK SMALL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F64":  "\u03C9\u0313\u0301",       //  " ὤ "  "GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA"                              GREEK SMALL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F65":  "\u03C9\u0314\u0301",       //  " ὥ "  "GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA"                              GREEK SMALL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F66":  "\u03C9\u0313\u0342",       //  " ὦ "  "GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI"                       GREEK SMALL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F67":  "\u03C9\u0314\u0342",       //  " ὧ "  "GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI"                       GREEK SMALL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F68":  "\u03A9\u0313",             //  " Ὠ "  "GREEK CAPITAL LETTER OMEGA WITH PSILI"                                     GREEK CAPITAL LETTER OMEGA  COMBINING COMMA ABOVE "\u1F69":  "\u03A9\u0314",             //  " Ὡ "  "GREEK CAPITAL LETTER OMEGA WITH DASIA"                                     GREEK CAPITAL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE "\u1F6A":  "\u03A9\u0313\u0300",       //  " Ὢ "  "GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA"                           GREEK CAPITAL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F6B":  "\u03A9\u0314\u0300",       //  " Ὣ "  "GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA"                           GREEK CAPITAL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT "\u1F6C":  "\u03A9\u0313\u0301",       //  " Ὤ "  "GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA"                            GREEK CAPITAL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F6D":  "\u03A9\u0314\u0301",       //  " Ὥ "  "GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA"                            GREEK CAPITAL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT "\u1F6E":  "\u03A9\u0313\u0342",       //  " Ὦ "  "GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI"                     GREEK CAPITAL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F6F":  "\u03A9\u0314\u0342",       //  " Ὧ "  "GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI"                     GREEK CAPITAL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI "\u1F70":  "\u03B1\u0300",             //  " ὰ "  "GREEK SMALL LETTER ALPHA WITH VARIA"                                       GREEK SMALL LETTER ALPHA  COMBINING GRAVE ACCENT "\u1F71":  "\u03B1\u0301",             //  " ά "  "GREEK SMALL LETTER ALPHA WITH OXIA"                                        GREEK SMALL LETTER ALPHA  COMBINING ACUTE ACCENT "\u1F72":  "\u03B5\u0300",             //  " ὲ "  "GREEK SMALL LETTER EPSILON WITH VARIA"                                     GREEK SMALL LETTER EPSILON  COMBINING GRAVE ACCENT "\u1F73":  "\u03B5\u0301",             //  " έ "  "GREEK SMALL LETTER EPSILON WITH OXIA"                                      GREEK SMALL LETTER EPSILON  COMBINING ACUTE ACCENT "\u1F74":  "\u03B7\u0300",             //  " ὴ "  "GREEK SMALL LETTER ETA WITH VARIA"                                         GREEK SMALL LETTER ETA  COMBINING GRAVE ACCENT "\u1F75":  "\u03B7\u0301",             //  " ή "  "GREEK SMALL LETTER ETA WITH OXIA"                                          GREEK SMALL LETTER ETA  COMBINING ACUTE ACCENT "\u1F76":  "\u03B9\u0300",             //  " ὶ "  "GREEK SMALL LETTER IOTA WITH VARIA"                                        GREEK SMALL LETTER IOTA  COMBINING GRAVE ACCENT "\u1F77":  "\u03B9\u0301",             //  " ί "  "GREEK SMALL LETTER IOTA WITH OXIA"                                         GREEK SMALL LETTER IOTA  COMBINING ACUTE ACCENT "\u1F78":  "\u03BF\u0300",             //  " ὸ "  "GREEK SMALL LETTER OMICRON WITH VARIA"                                     GREEK SMALL LETTER OMICRON  COMBINING GRAVE ACCENT "\u1F79":  "\u03BF\u0301",             //  " ό "  "GREEK SMALL LETTER OMICRON WITH OXIA"                                      GREEK SMALL LETTER OMICRON  COMBINING ACUTE ACCENT "\u1F7A":  "\u03C5\u0300",             //  " ὺ "  "GREEK SMALL LETTER UPSILON WITH VARIA"                                     GREEK SMALL LETTER UPSILON  COMBINING GRAVE ACCENT "\u1F7B":  "\u03C5\u0301",             //  " ύ "  "GREEK SMALL LETTER UPSILON WITH OXIA"                                      GREEK SMALL LETTER UPSILON  COMBINING ACUTE ACCENT "\u1F7C":  "\u03C9\u0300",             //  " ὼ "  "GREEK SMALL LETTER OMEGA WITH VARIA"                                       GREEK SMALL LETTER OMEGA  COMBINING GRAVE ACCENT "\u1F7D":  "\u03C9\u0301",             //  " ώ "  "GREEK SMALL LETTER OMEGA WITH OXIA"                                        GREEK SMALL LETTER OMEGA  COMBINING ACUTE ACCENT "\u1F80":  "\u03B1\u0313\u0345",       //  " ᾀ "  "GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI"                     GREEK SMALL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING GREEK YPOGEGRAMMENI "\u1F81":  "\u03B1\u0314\u0345",       //  " ᾁ "  "GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI"                     GREEK SMALL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK YPOGEGRAMMENI "\u1F82":  "\u03B1\u0313\u0300\u0345", //  " ᾂ "  "GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI"           GREEK SMALL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F83":  "\u03B1\u0314\u0300\u0345", //  " ᾃ "  "GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI"           GREEK SMALL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F84":  "\u03B1\u0313\u0301\u0345", //  " ᾄ "  "GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI"            GREEK SMALL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F85":  "\u03B1\u0314\u0301\u0345", //  " ᾅ "  "GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI"            GREEK SMALL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F86":  "\u03B1\u0313\u0342\u0345", //  " ᾆ "  "GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI"     GREEK SMALL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1F87":  "\u03B1\u0314\u0342\u0345", //  " ᾇ "  "GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI"     GREEK SMALL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1F88":  "\u0391\u0313\u0345",       //  " ᾈ "  "GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI"                  GREEK CAPITAL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING GREEK YPOGEGRAMMENI "\u1F89":  "\u0391\u0314\u0345",       //  " ᾉ "  "GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI"                  GREEK CAPITAL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK YPOGEGRAMMENI "\u1F8A":  "\u0391\u0313\u0300\u0345", //  " ᾊ "  "GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI"        GREEK CAPITAL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F8B":  "\u0391\u0314\u0300\u0345", //  " ᾋ "  "GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI"        GREEK CAPITAL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F8C":  "\u0391\u0313\u0301\u0345", //  " ᾌ "  "GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI"         GREEK CAPITAL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F8D":  "\u0391\u0314\u0301\u0345", //  " ᾍ "  "GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI"         GREEK CAPITAL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F8E":  "\u0391\u0313\u0342\u0345", //  " ᾎ "  "GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI"  GREEK CAPITAL LETTER ALPHA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1F8F":  "\u0391\u0314\u0342\u0345", //  " ᾏ "  "GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI"  GREEK CAPITAL LETTER ALPHA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1F90":  "\u03B7\u0313\u0345",       //  " ᾐ "  "GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI"                       GREEK SMALL LETTER ETA  COMBINING COMMA ABOVE  COMBINING GREEK YPOGEGRAMMENI "\u1F91":  "\u03B7\u0314\u0345",       //  " ᾑ "  "GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI"                       GREEK SMALL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK YPOGEGRAMMENI "\u1F92":  "\u03B7\u0313\u0300\u0345", //  " ᾒ "  "GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI"             GREEK SMALL LETTER ETA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F93":  "\u03B7\u0314\u0300\u0345", //  " ᾓ "  "GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI"             GREEK SMALL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F94":  "\u03B7\u0313\u0301\u0345", //  " ᾔ "  "GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI"              GREEK SMALL LETTER ETA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F95":  "\u03B7\u0314\u0301\u0345", //  " ᾕ "  "GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI"              GREEK SMALL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F96":  "\u03B7\u0313\u0342\u0345", //  " ᾖ "  "GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI"       GREEK SMALL LETTER ETA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1F97":  "\u03B7\u0314\u0342\u0345", //  " ᾗ "  "GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI"       GREEK SMALL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1F98":  "\u0397\u0313\u0345",       //  " ᾘ "  "GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI"                    GREEK CAPITAL LETTER ETA  COMBINING COMMA ABOVE  COMBINING GREEK YPOGEGRAMMENI "\u1F99":  "\u0397\u0314\u0345",       //  " ᾙ "  "GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI"                    GREEK CAPITAL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK YPOGEGRAMMENI "\u1F9A":  "\u0397\u0313\u0300\u0345", //  " ᾚ "  "GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI"          GREEK CAPITAL LETTER ETA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F9B":  "\u0397\u0314\u0300\u0345", //  " ᾛ "  "GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI"          GREEK CAPITAL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F9C":  "\u0397\u0313\u0301\u0345", //  " ᾜ "  "GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI"           GREEK CAPITAL LETTER ETA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F9D":  "\u0397\u0314\u0301\u0345", //  " ᾝ "  "GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI"           GREEK CAPITAL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1F9E":  "\u0397\u0313\u0342\u0345", //  " ᾞ "  "GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI"    GREEK CAPITAL LETTER ETA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1F9F":  "\u0397\u0314\u0342\u0345", //  " ᾟ "  "GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI"    GREEK CAPITAL LETTER ETA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1FA0":  "\u03C9\u0313\u0345",       //  " ᾠ "  "GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI"                     GREEK SMALL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING GREEK YPOGEGRAMMENI "\u1FA1":  "\u03C9\u0314\u0345",       //  " ᾡ "  "GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI"                     GREEK SMALL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK YPOGEGRAMMENI "\u1FA2":  "\u03C9\u0313\u0300\u0345", //  " ᾢ "  "GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI"           GREEK SMALL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FA3":  "\u03C9\u0314\u0300\u0345", //  " ᾣ "  "GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI"           GREEK SMALL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FA4":  "\u03C9\u0313\u0301\u0345", //  " ᾤ "  "GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI"            GREEK SMALL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FA5":  "\u03C9\u0314\u0301\u0345", //  " ᾥ "  "GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI"            GREEK SMALL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FA6":  "\u03C9\u0313\u0342\u0345", //  " ᾦ "  "GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI"     GREEK SMALL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1FA7":  "\u03C9\u0314\u0342\u0345", //  " ᾧ "  "GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI"     GREEK SMALL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1FA8":  "\u03A9\u0313\u0345",       //  " ᾨ "  "GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI"                  GREEK CAPITAL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING GREEK YPOGEGRAMMENI "\u1FA9":  "\u03A9\u0314\u0345",       //  " ᾩ "  "GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI"                  GREEK CAPITAL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK YPOGEGRAMMENI "\u1FAA":  "\u03A9\u0313\u0300\u0345", //  " ᾪ "  "GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI"        GREEK CAPITAL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FAB":  "\u03A9\u0314\u0300\u0345", //  " ᾫ "  "GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI"        GREEK CAPITAL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FAC":  "\u03A9\u0313\u0301\u0345", //  " ᾬ "  "GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI"         GREEK CAPITAL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FAD":  "\u03A9\u0314\u0301\u0345", //  " ᾭ "  "GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI"         GREEK CAPITAL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FAE":  "\u03A9\u0313\u0342\u0345", //  " ᾮ "  "GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI"  GREEK CAPITAL LETTER OMEGA  COMBINING COMMA ABOVE  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1FAF":  "\u03A9\u0314\u0342\u0345", //  " ᾯ "  "GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI"  GREEK CAPITAL LETTER OMEGA  COMBINING REVERSED COMMA ABOVE  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1FB0":  "\u03B1\u0306",             //  " ᾰ "  "GREEK SMALL LETTER ALPHA WITH VRACHY"                                      GREEK SMALL LETTER ALPHA  COMBINING BREVE "\u1FB1":  "\u03B1\u0304",             //  " ᾱ "  "GREEK SMALL LETTER ALPHA WITH MACRON"                                      GREEK SMALL LETTER ALPHA  COMBINING MACRON "\u1FB2":  "\u03B1\u0300\u0345",       //  " ᾲ "  "GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI"                     GREEK SMALL LETTER ALPHA  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FB3":  "\u03B1\u0345",             //  " ᾳ "  "GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI"                               GREEK SMALL LETTER ALPHA  COMBINING GREEK YPOGEGRAMMENI "\u1FB4":  "\u03B1\u0301\u0345",       //  " ᾴ "  "GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI"                      GREEK SMALL LETTER ALPHA  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FB6":  "\u03B1\u0342",             //  " ᾶ "  "GREEK SMALL LETTER ALPHA WITH PERISPOMENI"                                 GREEK SMALL LETTER ALPHA  COMBINING GREEK PERISPOMENI "\u1FB7":  "\u03B1\u0342\u0345",       //  " ᾷ "  "GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI"               GREEK SMALL LETTER ALPHA  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1FB8":  "\u0391\u0306",             //  " Ᾰ "  "GREEK CAPITAL LETTER ALPHA WITH VRACHY"                                    GREEK CAPITAL LETTER ALPHA  COMBINING BREVE "\u1FB9":  "\u0391\u0304",             //  " Ᾱ "  "GREEK CAPITAL LETTER ALPHA WITH MACRON"                                    GREEK CAPITAL LETTER ALPHA  COMBINING MACRON "\u1FBA":  "\u0391\u0300",             //  " Ὰ "  "GREEK CAPITAL LETTER ALPHA WITH VARIA"                                     GREEK CAPITAL LETTER ALPHA  COMBINING GRAVE ACCENT "\u1FBB":  "\u0391\u0301",             //  " Ά "  "GREEK CAPITAL LETTER ALPHA WITH OXIA"                                      GREEK CAPITAL LETTER ALPHA  COMBINING ACUTE ACCENT "\u1FBC":  "\u0391\u0345",             //  " ᾼ "  "GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI"                            GREEK CAPITAL LETTER ALPHA  COMBINING GREEK YPOGEGRAMMENI //"\u1FBD":  "",                         //  " ᾽ "  "GREEK KORONIS" "\u1FBE":  "\u03B9",                   //  " ι "  "GREEK PROSGEGRAMMENI"                                                      GREEK SMALL LETTER IOTA //"\u1FBF":  "",                         //  " ᾿ "  "GREEK PSILI" //"\u1FC0":  "",                         //  " ῀ "  "GREEK PERISPOMENI" "\u1FC1":  "\u00A8\u0342",             //  " ῁ "  "GREEK DIALYTIKA AND PERISPOMENI"                                           DIAERESIS  COMBINING GREEK PERISPOMENI "\u1FC2":  "\u03B7\u0300\u0345",       //  " ῂ "  "GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI"                       GREEK SMALL LETTER ETA  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FC3":  "\u03B7\u0345",             //  " ῃ "  "GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI"                                 GREEK SMALL LETTER ETA  COMBINING GREEK YPOGEGRAMMENI "\u1FC4":  "\u03B7\u0301\u0345",       //  " ῄ "  "GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI"                        GREEK SMALL LETTER ETA  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FC6":  "\u03B7\u0342",             //  " ῆ "  "GREEK SMALL LETTER ETA WITH PERISPOMENI"                                   GREEK SMALL LETTER ETA  COMBINING GREEK PERISPOMENI "\u1FC7":  "\u03B7\u0342\u0345",       //  " ῇ "  "GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI"                 GREEK SMALL LETTER ETA  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1FC8":  "\u0395\u0300",             //  " Ὲ "  "GREEK CAPITAL LETTER EPSILON WITH VARIA"                                   GREEK CAPITAL LETTER EPSILON  COMBINING GRAVE ACCENT "\u1FC9":  "\u0395\u0301",             //  " Έ "  "GREEK CAPITAL LETTER EPSILON WITH OXIA"                                    GREEK CAPITAL LETTER EPSILON  COMBINING ACUTE ACCENT "\u1FCA":  "\u0397\u0300",             //  " Ὴ "  "GREEK CAPITAL LETTER ETA WITH VARIA"                                       GREEK CAPITAL LETTER ETA  COMBINING GRAVE ACCENT "\u1FCB":  "\u0397\u0301",             //  " Ή "  "GREEK CAPITAL LETTER ETA WITH OXIA"                                        GREEK CAPITAL LETTER ETA  COMBINING ACUTE ACCENT "\u1FCC":  "\u0397\u0345",             //  " ῌ "  "GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI"                              GREEK CAPITAL LETTER ETA  COMBINING GREEK YPOGEGRAMMENI "\u1FCD":  "\u1FBF\u0300",             //  " ῍ "  "GREEK PSILI AND VARIA"                                                     GREEK PSILI  COMBINING GRAVE ACCENT "\u1FCE":  "\u1FBF\u0301",             //  " ῎ "  "GREEK PSILI AND OXIA"                                                      GREEK PSILI  COMBINING ACUTE ACCENT "\u1FCF":  "\u1FBF\u0342",             //  " ῏ "  "GREEK PSILI AND PERISPOMENI"                                               GREEK PSILI  COMBINING GREEK PERISPOMENI "\u1FD0":  "\u03B9\u0306",             //  " ῐ "  "GREEK SMALL LETTER IOTA WITH VRACHY"                                       GREEK SMALL LETTER IOTA  COMBINING BREVE "\u1FD1":  "\u03B9\u0304",             //  " ῑ "  "GREEK SMALL LETTER IOTA WITH MACRON"                                       GREEK SMALL LETTER IOTA  COMBINING MACRON "\u1FD2":  "\u03B9\u0308\u0300",       //  " ῒ "  "GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA"                          GREEK SMALL LETTER IOTA  COMBINING DIAERESIS  COMBINING GRAVE ACCENT "\u1FD3":  "\u03B9\u0308\u0301",       //  " ΐ "  "GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA"                           GREEK SMALL LETTER IOTA  COMBINING DIAERESIS  COMBINING ACUTE ACCENT "\u1FD6":  "\u03B9\u0342",             //  " ῖ "  "GREEK SMALL LETTER IOTA WITH PERISPOMENI"                                  GREEK SMALL LETTER IOTA  COMBINING GREEK PERISPOMENI "\u1FD7":  "\u03B9\u0308\u0342",       //  " ῗ "  "GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI"                    GREEK SMALL LETTER IOTA  COMBINING DIAERESIS  COMBINING GREEK PERISPOMENI "\u1FD8":  "\u0399\u0306",             //  " Ῐ "  "GREEK CAPITAL LETTER IOTA WITH VRACHY"                                     GREEK CAPITAL LETTER IOTA  COMBINING BREVE "\u1FD9":  "\u0399\u0304",             //  " Ῑ "  "GREEK CAPITAL LETTER IOTA WITH MACRON"                                     GREEK CAPITAL LETTER IOTA  COMBINING MACRON "\u1FDA":  "\u0399\u0300",             //  " Ὶ "  "GREEK CAPITAL LETTER IOTA WITH VARIA"                                      GREEK CAPITAL LETTER IOTA  COMBINING GRAVE ACCENT "\u1FDB":  "\u0399\u0301",             //  " Ί "  "GREEK CAPITAL LETTER IOTA WITH OXIA"                                       GREEK CAPITAL LETTER IOTA  COMBINING ACUTE ACCENT "\u1FDD":  "\u1FFE\u0300",             //  " ῝ "  "GREEK DASIA AND VARIA"                                                     GREEK DASIA  COMBINING GRAVE ACCENT "\u1FDE":  "\u1FFE\u0301",             //  " ῞ "  "GREEK DASIA AND OXIA"                                                      GREEK DASIA  COMBINING ACUTE ACCENT "\u1FDF":  "\u1FFE\u0342",             //  " ῟ "  "GREEK DASIA AND PERISPOMENI"                                               GREEK DASIA  COMBINING GREEK PERISPOMENI "\u1FE0":  "\u03C5\u0306",             //  " ῠ "  "GREEK SMALL LETTER UPSILON WITH VRACHY"                                    GREEK SMALL LETTER UPSILON  COMBINING BREVE "\u1FE1":  "\u03C5\u0304",             //  " ῡ "  "GREEK SMALL LETTER UPSILON WITH MACRON"                                    GREEK SMALL LETTER UPSILON  COMBINING MACRON "\u1FE2":  "\u03C5\u0308\u0300",       //  " ῢ "  "GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA"                       GREEK SMALL LETTER UPSILON  COMBINING DIAERESIS  COMBINING GRAVE ACCENT "\u1FE3":  "\u03C5\u0308\u0301",       //  " ΰ "  "GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA"                        GREEK SMALL LETTER UPSILON  COMBINING DIAERESIS  COMBINING ACUTE ACCENT "\u1FE4":  "\u03C1\u0313",             //  " ῤ "  "GREEK SMALL LETTER RHO WITH PSILI"                                         GREEK SMALL LETTER RHO  COMBINING COMMA ABOVE "\u1FE5":  "\u03C1\u0314",             //  " ῥ "  "GREEK SMALL LETTER RHO WITH DASIA"                                         GREEK SMALL LETTER RHO  COMBINING REVERSED COMMA ABOVE "\u1FE6":  "\u03C5\u0342",             //  " ῦ "  "GREEK SMALL LETTER UPSILON WITH PERISPOMENI"                               GREEK SMALL LETTER UPSILON  COMBINING GREEK PERISPOMENI "\u1FE7":  "\u03C5\u0308\u0342",       //  " ῧ "  "GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI"                 GREEK SMALL LETTER UPSILON  COMBINING DIAERESIS  COMBINING GREEK PERISPOMENI "\u1FE8":  "\u03A5\u0306",             //  " Ῠ "  "GREEK CAPITAL LETTER UPSILON WITH VRACHY"                                  GREEK CAPITAL LETTER UPSILON  COMBINING BREVE "\u1FE9":  "\u03A5\u0304",             //  " Ῡ "  "GREEK CAPITAL LETTER UPSILON WITH MACRON"                                  GREEK CAPITAL LETTER UPSILON  COMBINING MACRON "\u1FEA":  "\u03A5\u0300",             //  " Ὺ "  "GREEK CAPITAL LETTER UPSILON WITH VARIA"                                   GREEK CAPITAL LETTER UPSILON  COMBINING GRAVE ACCENT "\u1FEB":  "\u03A5\u0301",             //  " Ύ "  "GREEK CAPITAL LETTER UPSILON WITH OXIA"                                    GREEK CAPITAL LETTER UPSILON  COMBINING ACUTE ACCENT "\u1FEC":  "\u03A1\u0314",             //  " Ῥ "  "GREEK CAPITAL LETTER RHO WITH DASIA"                                       GREEK CAPITAL LETTER RHO  COMBINING REVERSED COMMA ABOVE "\u1FED":  "\u00A8\u0300",             //  " ῭ "  "GREEK DIALYTIKA AND VARIA"                                                 DIAERESIS  COMBINING GRAVE ACCENT "\u1FEE":  "\u00A8\u0301",             //  " ΅ "  "GREEK DIALYTIKA AND OXIA"                                                  DIAERESIS  COMBINING ACUTE ACCENT "\u1FEF":  "\u0060",                   //  " ` "  "GREEK VARIA"                                                               GRAVE ACCENT "\u1FF2":  "\u03C9\u0300\u0345",       //  " ῲ "  "GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI"                     GREEK SMALL LETTER OMEGA  COMBINING GRAVE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FF3":  "\u03C9\u0345",             //  " ῳ "  "GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI"                               GREEK SMALL LETTER OMEGA  COMBINING GREEK YPOGEGRAMMENI "\u1FF4":  "\u03C9\u0301\u0345",       //  " ῴ "  "GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI"                      GREEK SMALL LETTER OMEGA  COMBINING ACUTE ACCENT  COMBINING GREEK YPOGEGRAMMENI "\u1FF6":  "\u03C9\u0342",             //  " ῶ "  "GREEK SMALL LETTER OMEGA WITH PERISPOMENI"                                 GREEK SMALL LETTER OMEGA  COMBINING GREEK PERISPOMENI "\u1FF7":  "\u03C9\u0342\u0345",       //  " ῷ "  "GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI"               GREEK SMALL LETTER OMEGA  COMBINING GREEK PERISPOMENI  COMBINING GREEK YPOGEGRAMMENI "\u1FF8":  "\u039F\u0300",             //  " Ὸ "  "GREEK CAPITAL LETTER OMICRON WITH VARIA"                                   GREEK CAPITAL LETTER OMICRON  COMBINING GRAVE ACCENT "\u1FF9":  "\u039F\u0301",             //  " Ό "  "GREEK CAPITAL LETTER OMICRON WITH OXIA"                                    GREEK CAPITAL LETTER OMICRON  COMBINING ACUTE ACCENT "\u1FFA":  "\u03A9\u0300",             //  " Ὼ "  "GREEK CAPITAL LETTER OMEGA WITH VARIA"                                     GREEK CAPITAL LETTER OMEGA  COMBINING GRAVE ACCENT "\u1FFB":  "\u03A9\u0301",             //  " Ώ "  "GREEK CAPITAL LETTER OMEGA WITH OXIA"                                      GREEK CAPITAL LETTER OMEGA  COMBINING ACUTE ACCENT "\u1FFC":  "\u03A9\u0345",             //  " ῼ "  "GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI"                            GREEK CAPITAL LETTER OMEGA  COMBINING GREEK YPOGEGRAMMENI "\u1FFD":  "\u00B4",                   //  " ´ "  "GREEK OXIA"                                                                ACUTE ACCENT //"\u1FFE":  "",                         //  " ῾ "  "GREEK DASIA" }

function swapKeysValues(obj) { var temp = {} Object.keys(obj).forEach(function (key) { temp[obj[key]] = key }) return temp }

return GreekBeta

})

// console.log('(1) I see BetaCodeElProcessor:', BetaCodeElProcessor)

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Utility routines

/** * Given integer value, return hexadecimal string with a fixed * number of digits, uppercased, and '0' filled from left. * The default number of digits is 4. * * @param {number} value * @param {number=} digits * @return {string} hexadecimal string for input value */

function asHex(value, digits) { if (undefined === digits) digits = 4 return value.toString(16).padStart(digits, '0').toUpperCase }

/** * Return 4 digit hexadecimal string for given integer value * * @param {number} value * @return {string} 4 digit hexadecimal string for input value */

function asHex4(value) { return asHex(value, 4) }

/** * Return 8 digit hexadecimal string for given integer value * * @param {number} value * @return {string} 8 digit hexadecimal string for input value */

function asHex8(value) { return asHex(value, 8) }

function codePointAsHex(codePoint) { if (codePoint < 0x10000) { return asHex4(codePoint) } else { return asHex(codePoint, 6) } }

/** * Return string of Unicode codepoints expressed in hexadecimal * Note focus on code points, and so surrogate pairs are processed * together as one character in range 010000-10FFFF. * * @param {string} str - string of Unicode characters * @return {string} - string of Unicode character hexadecimal escapes */

function codePointsAsUnicodeEscapes(str) { //console.log(' codePointsAsUnicodeEscapes: str "'+str+'"') var result = ""    //  as hex char codes

var chars = str.split('') for (var idx=0; idx < chars.length; idx++) { var char = chars[idx] //console.log(' codePointsAsUnicodeEscapes: char "'+char+'"') var codePoint = char.codePointAt(0) var asHex = codePointAsHex(codePoint) result += '\\u'+asHex }

return result }

function isPlainObject(v) { return typeof v === 'object' && v != null && !Array.isArray(v) }

// -

// IME front-end implementing Beta code input of Ancient Greek characters //   IME == επεξεργαστής μεθόδων εισαγωγής -> EME

var BetaCodeElEME = (function {

// These are the configuration defaults that users should consider // updating to their preferences. // See documentation for creating user config BetaCodeElUserConfig

var configDefaults = { // in lieu of fancy buttons, input this string to activate this IME activationMatchString: ')zcqp(', activationMatchString: 'zzz',

// How to show in-progress status of Beta Code character composition

// Displayed in a floating status box displayStatusBox: true,

// Displayed inline with existing text within text box // Whether in-progress greek character should be displayed displayInlineStatusGreek: true, // Whether in-progress Beta Code input characters should be displayed displayInlineStatusBetaCode: true,

// Configuration values for Beta Code IME engine

isStrict: false,

preferGreekExtended: true, preferPrecomposed: true, }

// Variables used while processing

var initialProps = { // If this module is allowed to run enabled: true,

// Whether IME is active and interacting with keyboard active:  false, activationCharsCountSeen: 0,

// The Beta Code key-by-key processor betaCodeEngine: null,

// Remember the DOM elements we touch in page // The Wikisource enclosing the enclosingDiv: null, // Editing area we interact with wpTextbox1: null, // Our magic status box created when activated statusBox: null,

// Floating status box processing // We allow the user to move the status box to different positions // within the window. Track the offset between mouse pointer position // and current window top-left corner. statusBoxOffsetX: null, statusBoxOffsetY: null,

// Inline status option processing // textbox cursor locations where inline status strings have been placed inlineStatusStart: null, inlineStatusEnd: null, // latest inline status string inlineStatusString: null,

// We sometimes need to know when the IME is still in-progress // composing the next Greek character betaCodeInComposition: false, betaCodeLatestGreek: null, //betaCodeLatestInput: null, }

function BetaCodeEL(callerConfig) { // Set effective config from our defaults, user config values, // and caller-supplied values var config = Object.assign({}, configDefaults) if (typeof BetaCodeElUserConfig !== 'undefined'        &&  isPlainObject(BetaCodeElUserConfig)) { config = Object.assign(config, BetaCodeElUserConfig) } if (callerConfig != undefined) { config = Object.assign(config, callerConfig || {}) } this.config = config

// Set initial state variables Object.assign(this, initialProps)

// Allow removeEventListener to find addEventListener functions this.onStatusDivMouseMove = this.onStatusDivMouseMove.bind(this) this.onStatusDivMouseUp  = this.onStatusDivMouseUp.bind(this)

//XXX interpret activation string requirements if (this.config.activationMatchString === null ||  !this.config.activationMatchString.length) { console.log('   config activation match string problems?') } //XXX What other critical config values to check or normalize? }

//XXX Currently we are using event keydown and the e.key value. //   (re)consider switching to textbox input event ?

BetaCodeEL.prototype.onKeydown = function (e) { this.showEvent(e) console.log('     selection  start "'+e.target.selectionStart+'"  end "'+e.target.selectionEnd+'"  direction "'+e.target.selectionDirection+'"') console.log('       value  "'+e.target.value+'"')

// We process only simple characters, so do nothing if //   any alt / meta / ctrl modifier keys are pressed if ( e.altKey || e.ctrlKey || e.metaKey) return

// While active, check for deactivation signal if (this.active &&  e.key === 'Escape') { this.deactivate // We've detected and used key for ourselves, don't pass on to system e.preventDefault }

if (this.active &&  e.key === 'Tab') { //XXX make  this.forceComposition if (this.betaCodeInComposition) { //XXX Force saving any active composition as-is //XXX    this.engineForce // rather, a 'force' is simply to use the latest in-progress // data and then do an engine 'clear' !

var inProgressComposition = this.betaCodeLatestGreek

// Discard any active composition and in-progress status this.statusBoxClear this.statusInlineClear this.betaCodeEngine.resetState

// We want to insert our output chars at current cursor position. // We want to leave the cursor following the added chars. var wpTextbox1 = this.wpTextbox1 wpTextbox1.setRangeText(        inProgressComposition,         wpTextbox1.selectionStart,         wpTextbox1.selectionEnd,         'end'      )

e.preventDefault } }

if (this.active &&  e.key === 'Backspace') { //XXX make  this.clearComposition if (this.betaCodeInComposition) { // Discard any active composition and in-progress status this.statusBoxClear this.statusInlineClear this.betaCodeEngine.resetState

e.preventDefault } }

// All characters we give to the IME should be simple characters, // and so of length 1 if (e.key == undefined ||  e.key.length !== 1) return

// If not already active, check for activation signal string if (!this.active) { var matchString = this.config.activationMatchString var expectedChar = matchString[this.activationCharsCountSeen] if (e.key === expectedChar) { this.activationCharsCountSeen += 1 if (this.activationCharsCountSeen === matchString.length) { this.activationStringErase this.activate e.preventDefault }   } else { this.activationCharsCountSeen = 0 }   return }

// Here we are active and this is a possible input key for IME

// Beta Code must sometimes process multiple input characters before // returning a resulting output Greek character. This means we can // receive results saying: //   - output this character (and we are done) //   - output this character (and call me again with pending char) //   - nothing to output     (but here are the intermediate results) // So we have a loop in case this input key needs to be re-submitted. // And we check the results inside the loop to output a character. // And we check results after the loop to update intermediate results.

var nextKey = e.key var results var pending = true while (pending) { results = this.betaCodeEngine.processNextInputKey(nextKey) console.log('   betaCode results:', results) if (results.output != undefined) { console.log('       output: "'+results.output+'"    '+codePointsAsUnicodeEscapes(results.output)+'') }   if (results.input != undefined) { console.log('       input:', results.input) }   if (results.greek != undefined) { console.log('       greek: "'+results.greek+'"    '+codePointsAsUnicodeEscapes(results.greek)+'') }   if (results.pending != undefined) { console.log('       pending: "'+results.pending+'"    '+codePointsAsUnicodeEscapes(results.pending)+'') }   // If there is a character to output, stuff it in the text area if (results.output != undefined) { // add IME engine output to text area at cursor. This output // may be more than one 'character' when config options ask // for combining characters, giving base char + combiner chars //console.log('     value prior  "'+e.target.value+'"') //console.log('     selection  start "'+e.target.selectionStart+'"  end "'+e.target.selectionEnd+'"  direction "'+e.target.selectionDirection+'"') this.statusBoxClear this.statusInlineClear

// We want to insert our output chars at current cursor position. // We want to leave the cursor following the added chars. e.target.setRangeText(        results.output,         e.target.selectionStart,         e.target.selectionEnd,         'end'      ) //console.log('     value after  "'+e.target.value+'"') //console.log('     selection  start "'+e.target.selectionStart+'"  end "'+e.target.selectionEnd+'"  direction "'+e.target.selectionDirection+'"') }   // Check if we need to again call Beta Code processor with a    // pending - yet to be processed fully - key pending = results.pending != undefined if (pending) { nextKey = results.pending console.log('     pending set so repeating Beta Code processor call with "'+results.pending+'"') }

}

this.betaCodeInComposition = false this.betaCodeLatestGreek  = null //this.betaCodeLatestInput  = null

//XXX Would really be nice to have more specific indications of when: //     - output generated //     - composition in-progress //   When there is no output (so that we've dropped out of above loop) //     is something still going on - in-progress? // Update engine to be more careful of results contents?

if (results.output == undefined) { //if (results.greek != undefined ||  results.input != undefined) { //XXX Is the above wrong? //XXX Is the below check entirely sufficient? if (results.greek != undefined) { this.statusBoxUpdate(results.greek, results.input)

this.betaCodeInComposition = true this.betaCodeLatestGreek  = results.greek //   this.betaCodeLatestInput   = results.input

this.statusInlineUpdate(results.greek, results.input) } }

//console.log(' onKeydown: betaCodeInComposition', this.betaCodeInComposition)

e.preventDefault }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// Inline status in-progress display helpers

BetaCodeEL.prototype.statusInlineClear = function { // Unless inline status currently displayed just return if (this.inlineStatusStart === null) return

//console.log('     selection  next  "'+e.target.selectionStart+'"  end "'+e.target.selectionEnd+'"  direction "'+e.target.selectionDirection+'"') this.wpTextbox1.setRangeText(    '',     this.inlineStatusStart,    this.inlineStatusEnd,    'end'  )

this.inlineStatusStart = null this.inlineStatusEnd  = null this.inlineStatusString = null

//XXX This is questionable and needs review //   The presumption is that in clearing we're also resetting in-progress //   state completely, as would be needed for backspace or escape this.betaCodeInComposition = false this.betaCodeLatestGreek  = null //this.betaCodeLatestInput  = null }

BetaCodeEL.prototype.statusInlineUpdate = function (greek, betaCode) { // Is inline status feature requested/configured by user? var displayInlineGreek = this.config.displayInlineStatusGreek var displayInlineBetaCode = this.config.displayInlineStatusBetaCode if (!displayInlineGreek &&  !displayInlineBetaCode) return

var statusString = '' if (displayInlineGreek) { statusString += greek } if (displayInlineBetaCode) { statusString += betaCode }

var wpTextbox1 = this.wpTextbox1

if (this.inlineStatusStart !== null) { // If we are replacing a previous instance of inline status string wpTextbox1.setRangeText(      statusString,       this.inlineStatusStart,      this.inlineStatusEnd,      'end'    ) } else { // If we are inserting a new inline status string this.inlineStatusStart = wpTextbox1.selectionEnd wpTextbox1.setRangeText(      statusString,       wpTextbox1.selectionStart,       wpTextbox1.selectionEnd,       'end'    ) }

this.inlineStatusEnd  = this.inlineStatusStart + statusString.length this.inlineStatusString = statusString }

// Activate our IME/UI processing // If floating status box feature is enable, display that

BetaCodeEL.prototype.activate = function { this.active = true this.activationCharsCountSeen = 0 this.statusBoxCreate }

BetaCodeEL.prototype.deactivate = function { // Deactivate ourselves

//XXX what else needed to deactivate? //   clean up any current composition, especially inline status

if (!this.active) return

// Discard any active composition and in-progress status //XXX this.statusBoxClear this.statusBoxDestroy this.statusInlineClear this.betaCodeEngine.resetState

this.active = false //console.log(' this.active:', this.active) //XXX should we overlay initial defaults onto 'this' again? }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// A kluge to remove the activation string from textbox

// Note that we are still in event handling, and the final char of //   the activation sequence has _not_ been stored into textbox. // Also, that the final char has already been checked as correct to //   enter activation, so we're checking only what can before that.

BetaCodeEL.prototype.activationStringErase = function { var wpTextbox1 = this.wpTextbox1

var selStart = wpTextbox1.selectionStart var selEnd  = wpTextbox1.selectionEnd var matchThis = this.config.activationMatchString.slice(0, -1)

// If there is actively selected text, quit if (selEnd !== selStart) return // If text prior to cursor not long enough, quit if (selEnd < matchThis.length) return

var chkStart = selEnd - matchThis.length var chkEnd = selEnd var checkingThis = wpTextbox1.value.slice(chkStart, chkEnd) // If textbox text matches the expected activation string if (checkingThis === matchThis) { wpTextbox1.setRangeText('', chkStart, chkEnd) //XXX Do we need/want the 4th argument 'end' ??? } }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// Status box in-progress display helpers

// We do everything manually here to minimize requirements from wikisource // environment. This will undoubtedly cause retching by the knowledgeable.

// Create a floating div that initially hovers over the text editing box // and displays current status of the IME interactions

BetaCodeEL.prototype.statusBoxCreate = function { // If not requested/configured by user, do nothing if (!this.config.displayStatusBox) { this.statusBox = null return }

if (this.statusBox) { statusBoxDestroy }

this.statusBox = null

// If we don't know page layout, quit if (!this.enclosingDiv ||  !this.wpTextbox1) return

// describe status box appearance var statusBoxBackgroundColor = 'hsl(180,100%,94%)' var statusBoxBorderColor = '#004C98'      // vs. 'blue' ?

//XXX height of the status box is tricky, as accents/diacritics can //   stack vertically. "w)=|" "i/+"  "w|"  are good tests   var statusBoxStyleBasic =     'display:flex;'     + 'align-items:center;'     + 'position:fixed;'    + 'width:7em;'     + 'height:2.25em;'    + 'border:2px '+statusBoxBorderColor+' solid;'     + 'border-radius:4px;' //  + 'background-color:'+statusBoxBackgroundColor+';'     + 'background: linear-gradient(to right,  rgba(0,0,0,0) 46%, white 46%, white 54%, rgba(0,0,0,0) 54%), linear-gradient(to bottom, rgba(0,0,0,0) 37%, white 37%, white 63%, rgba(0,0,0,0) 63%), rgb(224, 255, 255);'  var statusBoxStyleGreek =     'pointer-events: none;'     + 'width:1.00em;'     + 'font-size:200%;'     + 'margin: 0 2px;'  var statusBoxStyleBeta =     'pointer-events: none;'     + 'flex:3;'     + 'font-size:150%;'    + 'margin: 0 4px;'  var statusBoxStyleGrab =    'pointer-events: none;'     + 'font-size:133%;'     + 'text-align:right;'     + 'margin-bottom: 2px;'

var statusSpan1 = document.createElement('span') statusSpan1.style = statusBoxStyleGreek statusSpan1.textContent = '\u1F6B' //XXX dummy content

var statusSpan2 = document.createElement('span') statusSpan2.style = statusBoxStyleBeta statusSpan2.textContent = 'W(\\'   //XXX dummy content

var statusSpan3 = document.createElement('span') statusSpan3.style = statusBoxStyleGrab statusSpan3.textContent = '||||'   // clumsily indicate a drag grab bar

var statusDiv = document.createElement('div') statusDiv.appendChild(statusSpan1) statusDiv.appendChild(statusSpan2) statusDiv.appendChild(statusSpan3)

// Decide initial positioning of the status box // If there is enough visible room above the text box, position like // we used to at text box top and a bit right of upper left corner. // Otherwise just fix at window top and middle of text box width. var style = statusBoxStyleBasic var rect = this.wpTextbox1.getBoundingClientRect var divX = Math.floor(rect.width / 2) + rect.left - 60 var divY = 0 if (rect.top > 38) { divX = 40              // a bit right of textbox top-left corner divY = rect.top - 36   // a bit up above first line of textbox } style += 'top:'  + divY + 'px;' style += 'left:' + divX + 'px;' statusDiv.style = style

this.enclosingDiv.appendChild(statusDiv)

statusDiv.addEventListener('mousedown',    this.onStatusDivMouseDown.bind(this))

this.statusBox = statusDiv }

BetaCodeEL.prototype.statusBoxDestroy = function { if (!this.statusBox) return

// If we don't know page layout, quit if (!this.enclosingDiv ||  !this.wpTextbox1) return

var statusDiv = this.statusBox statusDiv.parentNode.removeChild(statusDiv)

//XXX Do we need to .removeEventListener('mousedown', ...) here?

this.statusBox = null }

// Update some or all of the status box text strings

BetaCodeEL.prototype.statusBoxUpdate = function (greek, beta) { if (!this.statusBox) return

var statusDiv = this.statusBox var spans = statusDiv.querySelectorAll('span') if (!spans ||  spans.length !== 3) return

if (greek != undefined) { spans[0].textContent = greek }

if (beta != undefined) { spans[1].textContent = beta } }

BetaCodeEL.prototype.statusBoxClear = function { this.statusBoxUpdate(, ) }

// Allow user to move the status box within the browser window

BetaCodeEL.prototype.onStatusDivMouseDown = function (e) { //this.showEvent(e)

this.statusBoxOffsetX = e.target.offsetLeft - e.clientX this.statusBoxOffsetY = e.target.offsetTop - e.clientY

document.addEventListener('mousemove', this.onStatusDivMouseMove) document.addEventListener('mouseup',  this.onStatusDivMouseUp)

e.preventDefault }

BetaCodeEL.prototype.onStatusDivMouseMove = function (e) { //this.showEvent(e)

if (e.target === this.statusBox) { if (this.statusBoxOffsetX !== null) { e.target.style.left = event.clientX + this.statusBoxOffsetX + 'px' e.target.style.top = event.clientY + this.statusBoxOffsetY + 'px' } }

e.preventDefault }

BetaCodeEL.prototype.onStatusDivMouseUp = function (e) { //this.showEvent(e)

if (e.target === this.statusBox) { if (this.statusBoxOffsetX !== null) { this.statusBoxOffsetX = null this.statusBoxOffsetY = null

document.removeEventListener('mousemove', this.onStatusDivMouseMove) document.removeEventListener('mouseup',  this.onStatusDivMouseUp) } }

e.preventDefault }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// Debugging helpers

BetaCodeEL.prototype.showEvent = function (e) { console.log(' event: '+e.type+'   target: '+e.target+' ', e) }

////BetaCodeEL.prototype.sawInputEvent = function (e) { //// showEvent(e) //// console.log('  input:  inputType "'+e.inputType+'"   data "'+e.data+'"   value "'+e.target.value+'"') //// console.log('      selection  start "'+e.target.selectionStart+'"  end "'+e.target.selectionEnd+'"  direction "'+e.target.selectionDirection+'"') ////}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// Externally called

// Check config, check page layout, setup variables BetaCodeEL.prototype.initialize = function { // If not allowed to run, quit if (!this.enabled) return

// Disable ourselves in case any checks below fail this.enabled = false

if (typeof BetaCodeElProcessor !== 'function') { console.error("BetaCodeEl IME UI can't find IME engine 'BetaCodeElProcessor'") return } // Discover the current layout of page display elements // // Layout for WikiSource proofreading pages as of now (2021/10) has // a text area for editing that has a unique id selector //     . // In addition we need to know the surrounding, which does not // have a unique id selector, but does (currently) have a unique // class reference. //       // If either of this assumptions changes we should refuse to run. var enclosingDiv = document.querySelector('div.prp-page-edit-body') if (!enclosingDiv) return var wpTextbox1 = enclosingDiv.querySelector('#wpTextbox1') if (!wpTextbox1) return

wpTextbox1.addEventListener('keydown', this.onKeydown.bind(this))

this.enclosingDiv = enclosingDiv this.wpTextbox1 = wpTextbox1

// All preconditions satisfied so mark ourselves as enabled this.enabled = true

this.betaCodeEngine = new BetaCodeElProcessor({   isStrict:             this.config.isStrict      || false,    preferGreekExtended:  this.preferGreekExtended  || true,    preferPrecomposed:    this.preferPrecomposed    || true,  }) console.log(' I see newly created betaCode obj:', this.betaCodeEngine) }

return BetaCodeEL

}) //  var BetaCodeElEME

// console.log('(2) I see BetaCodeElEME:', BetaCodeElEME) // console.log('(3) I see BetaCodeElVersion:', BetaCodeElVersion)

//XXX // So with further understanding of selection APIs we can see that // two different modes of processing Beta input are possible. // //  The original idea was to capture keys on keydown and display them // in the status box until determining what to insert into textarea. // Thus nothing but *output* result chars would be added to the text // in the textarea. // Beta input never appears in textarea in this mode. // Intermediate Greek forms show up only in status until complete. // //  But since we can examine the recently entered text by peeking into // the textarea, and then replace recent text with output when Beta // input is detected and understood, we could allow replacement with // output Greek chars. // Beta input appears in textarea and then is replaced by output Greek. // Intermediate Greek forms show up preceding Beta input until all // that is replaced with final Greek output. // We would have to track where the beginning of the current text // segment is - temp Greek followed by Beta - and then replace with // the final Greek char. // //  One important factor is that displaying the status box allows us to //  show the intermediate Greek char *enlarged*, making it easier to see // if the correct char is being created. // A char being built within textarea has a small fontsize usually, and // can be very hard to read as to accents and breathing marks. // However, the status box can be used with both setups. Yes? // // A further ramification is that processing Greek/Beta within textarea // means that using the textarea 'input' event becomes practical. That // might be less tricky than correctly handling keydown events in all // situations.

$(document).ready( function {  pageSpaceNumberHere = mw.config.get('wgNamespaceIds')['page']  inPageSpace = mw.config.get('wgNamespaceNumber') === pageSpaceNumberHere;

wikiIDHere = mw.config.get('wgWikiID') onWikisource = wikiIDHere === 'enwikisource' ||  wikiIDHere === 'elwikisource'

inEditSubmitMode = mw.config.get('wgAction') === 'edit' || mw.config.get('wgAction') === 'submit'; hasTextbox = document.querySelector('#wpTextbox1') != null; hasEnclosingDiv = document.querySelector('div.prp-page-edit-body') != null;

console.log( '   inPageSpace:     ', inPageSpace); console.log( '   onWikisource:    ', onWikisource); console.log( '   inEditSubmitMode:', inEditSubmitMode); console.log( '   hasTextbox:      ', hasTextbox); console.log( '   hasEnclosingDiv: ', hasEnclosingDiv); if (inPageSpace &&  onWikisource  &&  inEditSubmitMode) { if (hasTextbox &&  hasEnclosingDiv) { if (typeof BetaCodeElEME !== 'undefined') { var BetaCodeElIME = new BetaCodeElEME console.log(' I see BetaCodeElIME is:', BetaCodeElIME) BetaCodeElIME.initialize } else { console.log(' BetaCodeElEME is not loaded:', typeof BetaCodeElEME) } 	}  } })