User:Shenme/BetaCodeIME.js

From Wikisource
Jump to navigation Jump to search
Note: After saving, changes may not occur immediately. Click here to learn how to bypass your browser's cache.
  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Cmd-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (Cmd-Shift-R on a Mac)
  • Internet Explorer: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Clear the cache in Tools → Preferences

For details and instructions about other browsers, see Wikipedia:Bypass your cache.

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  ’
//
//             —          _      Dash from underline        2014
//                                  U+2014  —  e2 80 94  EM DASH   —
//
//             ʹ          #      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  ’
//??  "-":  "-",        //  Hyphen              ‐ 2010    - 002D
//    "—":  "\u2014",   //  Dash                — 2014  EM DASH           —
//    "#":  "\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 ‐ or ‐)
//
//
// 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 <div> enclosing the <textarea>
  enclosingDiv: null,
  // Editing area <textarea> 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 
  //      <textarea id="wpTextbox1" ...>. 
  // In addition we need to know the surrounding <div>, which does not
  // have a unique id selector, but does (currently) have a unique
  // class reference. 
  //      <div class="prp-page-edit-body">
  // 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)
      }
  	}
  }
})