User:Shenme/BetaCodeIME.js
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.
Code that you insert on this page could contain malicious content capable of compromising your account. If you are unsure whether code you are adding to this page is safe, you can ask at the central discussion page, Scriptorium. The code will be executed when previewing this page under some skins, including Monobook. You can in the interim if you wish to refresh the content sooner under another skin. |
This script seems to have a documentation page at User:Shenme/BetaCodeIME. |
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 ’
"_": "\u2014", // Dash from underline _ -> — 2014 EM DASH —
"#": "\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)
}
}
}
})