singlish-to-sinhala
Version:
Real-time Singlish to Sinhala text conversion library for web input fields
1,135 lines (981 loc) • 59.6 kB
JavaScript
'use strict';
// Singlish to Sinhala conversion core functionality
// Character mapping and input processing logic
// Import the sinhalaPatternMap or define it inline
const sinhalaPatternMap = {
// Independent vowels
"a": "අ", "aa": "ආ", "A": "ඇ", "Aa": "ඈ", "AA": "ඈ", "i": "ඉ", "ii": "ඊ",
"u": "උ", "uu": "ඌ", "R": "ඍ", "Ru": "ඎ", "e": "එ", "ee": "ඒ", "ai": "ඓ",
"o": "ඔ", "oo": "ඕ", "au": "ඖ", "ou": "ඖ",
// Special characters and markers
"x": "ං", "z": "ං", "X": "ඞ", "H": "ඃ", "Z": "්",
// KA group - all combinations
"k": "ක්", "ka": "ක", "kha": "ඛ", "kaa": "කා", "kA": "කැ", "kAa": "කෑ", "kAA": "කෑ",
"ki": "කි", "kii": "කී", "ku": "කු", "kuu": "කූ", "kru": "කෘ", "ke": "කෙ", "kee": "කේ",
"kai": "කෛ", "ko": "කො", "koo": "කෝ", "kau": "කෞ", "kax": "කං", "kaz": "කං",
"kaH": "කඃ", "kya": "ක්ය", "kra": "ක්ර",
"kraa": "ක්රා", "krA": "ක්රැ", "krAa": "ක්රෑ", "krAA": "ක්රෑ",
"kri": "ක්රි", "krii": "ක්රී", "krI": "ක්රී", "kru": "ක්රු", "kruu": "ක්රූ", "krU": "ක්රූ",
"kre": "ක්රෙ", "kree": "ක්රේ", "krE": "ක්රේ", "krai": "ක්රෛ",
"kro": "ක්රො", "kroo": "ක්රෝ", "krO": "ක්රෝ", "krau": "ක්රෞ", "krou": "ක්රෞ",
"kh": "ඛ්", "kha": "ඛ", "khaa": "ඛා", "khA": "ඛැ", "khAa": "ඛෑ", "khAA": "ඛෑ",
"khi": "ඛි", "khii": "ඛී", "khI": "ඛී", "khu": "ඛු", "khuu": "ඛූ", "khU": "ඛූ",
"khR": "ඛෘ", "khRu": "ඛෲ",
"khe": "ඛෙ", "khee": "ඛේ", "khE": "ඛේ", "khai": "ඛෛ",
"kho": "ඛො", "khoo": "ඛෝ", "khO": "ඛෝ", "khau": "ඛෞ", "khou": "ඛෞ",
"khax": "ඛං", "khaz": "ඛං", "khaH": "ඛඃ",
"khya": "ඛ්ය", "khYa": "ඛ්ය", "khra": "ඛ්ර", "khRa": "ඛ්ර",
"khraa": "ඛ්රා", "khrA": "ඛ්රැ", "khrAa": "ඛ්රෑ", "khrAA": "ඛ්රෑ",
"khri": "ඛ්රි", "khrii": "ඛ්රී", "khrI": "ඛ්රී", "khru": "ඛ්රු", "khruu": "ඛ්රූ", "khrU": "ඛ්රූ",
"khre": "ඛ්රෙ", "khree": "ඛ්රේ", "khrE": "ඛ්රේ", "khrai": "ඛ්රෛ",
"khro": "ඛ්රො", "khroo": "ඛ්රෝ", "khrO": "ඛ්රෝ", "khrau": "ඛ්රෞ", "khrou": "ඛ්රෞ",
"K": "ඛ්", "Ka": "ඛ", "Kaa": "ඛා",
// GA group - all combinations
"g": "ග්", "ga": "ග", "gha": "ඝ", "gaa": "ගා", "gA": "ගැ", "gAa": "ගෑ", "gAA": "ගෑ",
"gi": "ගි", "gii": "ගී", "gu": "ගු", "guu": "ගූ", "gru": "ගෘ", "ge": "ගෙ", "gee": "ගේ",
"gai": "ගෛ", "go": "ගො", "goo": "ගෝ", "gau": "ගෞ", "gax": "ගං", "gaz": "ගං",
"gaH": "ගඃ", "gya": "ග්ය", "gra": "ග්ර",
"graa": "ග්රා", "grA": "ග්රැ", "grAa": "ග්රෑ", "grAA": "ග්රෑ",
"gri": "ග්රි", "grii": "ග්රී", "grI": "ග්රී", "gru": "ග්රු", "gruu": "ග්රූ", "grU": "ග්රූ",
"gre": "ග්රෙ", "gree": "ග්රේ", "grE": "ග්රේ", "grai": "ග්රෛ",
"gro": "ග්රො", "groo": "ග්රෝ", "grO": "ග්රෝ", "grau": "ග්රෞ", "grou": "ග්රෞ",
"gh": "ඝ්", "gha": "ඝ", "ghaa": "ඝා", "ghA": "ඝැ", "ghAa": "ඝෑ", "ghAA": "ඝෑ",
"ghi": "ඝි", "ghii": "ඝී", "ghI": "ඝී", "ghu": "ඝු", "ghuu": "ඝූ", "ghU": "ඝූ",
"ghR": "ඝෘ", "ghRu": "ඝෲ",
"ghe": "ඝෙ", "ghee": "ඝේ", "ghE": "ඝේ", "ghai": "ඝෛ",
"gho": "ඝො", "ghoo": "ඝෝ", "ghO": "ඝෝ", "ghau": "ඝෞ", "ghou": "ඝෞ",
"ghax": "ඝං", "ghaz": "ඝං", "ghaH": "ඝඃ",
"ghya": "ඝ්ය", "ghYa": "ඝ්ය", "ghra": "ඝ්ර", "ghRa": "ඝ්ර",
"ghraa": "ඝ්රා", "ghrA": "ඝ්රැ", "ghrAa": "ඝ්රෑ", "ghrAA": "ඝ්රෑ",
"ghri": "ඝ්රි", "ghrii": "ඝ්රී", "ghrI": "ඝ්රී", "ghru": "ඝ්රු", "ghruu": "ඝ්රූ", "ghrU": "ඝ්රූ",
"ghre": "ඝ්රෙ", "ghree": "ඝ්රේ", "ghrE": "ඝ්රේ", "ghrai": "ඝ්රෛ",
"ghro": "ඝ්රො", "ghroo": "ඝ්රෝ", "ghrO": "ඝ්රෝ", "ghrau": "ඝ්රෞ", "ghrou": "ඝ්රෞ",
"G": "ඝ්", "Ga": "ඝ", "Gaa": "ඝා",
"zG": "ඟ්", "zGa": "ඟ", "zGaa": "ඟා", "zGA": "ඟැ", "zGAa": "ඟෑ", "zGAA": "ඟෑ",
"zGi": "ඟි", "zGii": "ඟී", "zGI": "ඟී", "zGu": "ඟු", "zGuu": "ඟූ", "zGU": "ඟූ",
"zGR": "ඟෘ", "zGRu": "ඟෲ",
"zGe": "ඟෙ", "zGee": "ඟේ", "zGE": "ඟේ", "zGai": "ඟෛ",
"zGo": "ඟො", "zGoo": "ඟෝ", "zGO": "ඟෝ", "zGau": "ඟෞ", "zGou": "ඟෞ",
"zGax": "ඟං", "zGaz": "ඟං", "zGaH": "ඟඃ",
"zGya": "ඟ්ය", "zGYa": "ඟ්ය", "zGra": "ඟ්ර", "zGRa": "ඟ්ර",
"zGraa": "ඟ්රා", "zGrA": "ඟ්රැ", "zGrAa": "ඟ්රෑ", "zGrAA": "ඟ්රෑ",
"zGri": "ඟ්රි", "zGrii": "ඟ්රී", "zGrI": "ඟ්රී", "zGru": "ඟ්රු", "zGruu": "ඟ්රූ", "zGrU": "ඟ්රූ",
"zGre": "ඟ්රෙ", "zGree": "ඟ්රේ", "zGrE": "ඟ්රේ", "zGrai": "ඟ්රෛ",
"zGro": "ඟ්රො", "zGroo": "ඟ්රෝ", "zGrO": "ඟ්රෝ", "zGrau": "ඟ්රෞ", "zGrou": "ඟ්රෞ",
// CHA group - all combinations
"ch": "ච්", "cha": "ච", "chha": "ඡ", "chaa": "චා", "chA": "චැ", "chAa": "චෑ", "chAA": "චෑ",
"chi": "චි", "chii": "චී", "chu": "චු", "chuu": "චූ", "chru": "චෘ", "che": "චෙ", "chee": "චේ",
"chai": "චෛ", "cho": "චො", "choo": "චෝ", "chau": "චෞ", "chax": "චං", "chaz": "චං",
"chaH": "චඃ", "chya": "ච්ය", "chra": "ච්ර",
"chraa": "ච්රා", "chrA": "ච්රැ", "chrAa": "ච්රෑ", "chrAA": "ච්රෑ",
"chri": "ච්රි", "chrii": "ච්රී", "chrI": "ච්රී", "chru": "ච්රු", "chruu": "ච්රූ", "chrU": "ච්රූ",
"chre": "ච්රෙ", "chree": "ච්රේ", "chrE": "ච්රේ", "chrai": "ච්රෛ",
"chro": "ච්රො", "chroo": "ච්රෝ", "chrO": "ච්රෝ", "chrau": "ච්රෞ", "chrou": "ච්රෞ",
"chh": "ඡ්", "chha": "ඡ", "chhaa": "ඡා", "chhA": "ඡැ", "chhAa": "ඡෑ", "chhAA": "ඡෑ",
"chhi": "ඡි", "chhii": "ඡී", "chhI": "ඡී", "chhu": "ඡු", "chhuu": "ඡූ", "chhU": "ඡූ",
"chhR": "ඡෘ", "chhRu": "ඡෲ",
"chhe": "ඡෙ", "chhee": "ඡේ", "chhE": "ඡේ", "chhai": "ඡෛ",
"chho": "ඡො", "chhoo": "ඡෝ", "chhO": "ඡෝ", "chhau": "ඡෞ", "chhou": "ඡෞ",
"chhax": "ඡං", "chhaz": "ඡං", "chhaH": "ඡඃ",
"chhya": "ඡ්ය", "chhYa": "ඡ්ය", "chhra": "ඡ්ර", "chhRa": "ඡ්ර",
"C": "ඡ්", "Ca": "ඡ", "Caa": "ඡා",
"zka": "ඤ", "zKa": "ඤ", "zk": "ඤ්", "zK": "ඤ්",
"zcha": "ඦ",
// JA group - all combinations
"j": "ජ්", "ja": "ජ", "jha": "ඣ", "jaa": "ජා", "jA": "ජැ", "jAa": "ජෑ", "jAA": "ජෑ",
"ji": "ජි", "jii": "ජී", "ju": "ජු", "juu": "ජූ", "jru": "ජෘ", "je": "ජෙ", "jee": "ජේ",
"jai": "ජෛ", "jo": "ජො", "joo": "ජෝ", "jau": "ජෞ", "jax": "ජං", "jaz": "ජං",
"jaH": "ජඃ", "jya": "ජ්ය", "jra": "ජ්ර",
"jh": "ඣ්", "jha": "ඣ", "jhaa": "ඣා", "jhA": "ඣැ", "jhAa": "ඣෑ", "jhAA": "ඣෑ",
"jhi": "ඣි", "jhii": "ඣී", "jhI": "ඣී", "jhu": "ඣු", "jhuu": "ඣූ", "jhU": "ඣූ",
"jhR": "ඣෘ", "jhRu": "ඣෲ",
"jhe": "ඣෙ", "jhee": "ඣේ", "jhE": "ඣේ", "jhai": "ඣෛ",
"jho": "ඣො", "jhoo": "ඣෝ", "jhO": "ඣෝ", "jhau": "ඣෞ", "jhou": "ඣෞ",
"jhax": "ඣං", "jhaz": "ඣං", "jhaH": "ඣඃ",
"jhya": "ඣ්ය", "jhYa": "ඣ්ය", "jhra": "ඣ්ර", "jhRa": "ඣ්ර",
"J": "ඣ්", "Ja": "ඣ", "Jaa": "ඣා",
"zJ": "ඦ්", "zja": "ඦ", "zJaa": "ඦා", "zJA": "ඦැ", "zJAa": "ඦෑ", "zJAA": "ඦෑ",
"zJi": "ඦි", "zJii": "ඦී", "zJI": "ඦී", "zJu": "ඦු", "zJuu": "ඦූ", "zJU": "ඦූ",
"zJR": "ඦෘ", "zJRu": "ඦෲ",
"zJe": "ඦෙ", "zJee": "ඦේ", "zJE": "ඦේ", "zJai": "ඦෛ",
"zJo": "ඦො", "zJoo": "ඦෝ", "zJO": "ඦෝ", "zJau": "ඦෞ", "zJou": "ඦෞ",
"zJax": "ඦං", "zJaz": "ඦං", "zJaH": "ඦඃ",
"zJya": "ඦ්ය", "zJYa": "ඦ්ය", "zJra": "ඦ්ර", "zJRa": "ඦ්ර",
"GYa": "ඥ", "jna": "ඥ", "gna": "ඥ", "zNa": "ඥ", "zha": "ඥ",
"GY": "ඥ්", "jn": "ඥ්", "gn": "ඥ්", "zN": "ඥ්", "zh": "ඥ්",
// TA group (retroflex) - all combinations
"t": "ට්", "ta": "ට", "Ta": "ඨ", "taa": "ටා", "tA": "ටැ", "tAa": "ටෑ", "tAA": "ටෑ",
"ti": "ටි", "tii": "ටී", "tu": "ටු", "tuu": "ටූ", "tru": "ටෘ", "te": "ටෙ", "tee": "ටේ",
"tai": "ටෛ", "to": "ටො", "too": "ටෝ", "tau": "ටෞ", "tax": "ටං", "taz": "ටං",
"taH": "ටඃ", "tya": "ට්ය", "tra": "ට්ර",
"T": "ඨ්", "Ta": "ඨ", "Taa": "ඨා", "TA": "ඨැ", "TAa": "ඨෑ", "TAA": "ඨෑ",
"Ti": "ඨි", "Tii": "ඨී", "TI": "ඨී", "Tu": "ඨු", "Tuu": "ඨූ", "TU": "ඨූ",
"TR": "ඨෘ", "TRu": "ඨෲ",
"Te": "ඨෙ", "Tee": "ඨේ", "TE": "ඨේ", "Tai": "ඨෛ",
"To": "ඨො", "Too": "ඨෝ", "TO": "ඨෝ", "Tau": "ඨෞ", "Tou": "ඨෞ",
"Tax": "ඨං", "Taz": "ඨං", "TaH": "ඨඃ",
"Tya": "ඨ්ය", "TYa": "ඨ්ය", "Tra": "ඨ්ර", "TRa": "ඨ්ර",
"TTa": "ට්ඨ",
// DA group (retroflex) - all combinations
"d": "ඩ්", "da": "ඩ", "Da": "ඪ", "daa": "ඩා", "dA": "ඩැ", "dAa": "ඩෑ", "dAA": "ඩෑ",
"di": "ඩි", "dii": "ඩී", "du": "ඩු", "duu": "ඩූ", "dru": "ඩෘ", "de": "ඩෙ", "dee": "ඩේ",
"dai": "ඩෛ", "do": "ඩො", "doo": "ඩෝ", "dau": "ඩෞ", "dax": "ඩං", "daz": "ඩං",
"daH": "ඩඃ", "dya": "ඩ්ය", "dra": "ඩ්ර", "D": "ඪ්", "Daa": "ඪා",
"zda": "ඬ", "zD": "ඬ්", "zDa": "ඬ",
"D": "ඪ්", "Da": "ඪ", "Daa": "ඪා", "DA": "ඪැ", "DAa": "ඪෑ", "DAA": "ඪෑ",
"Di": "ඪි", "Dii": "ඪී", "DI": "ඪී", "Du": "ඪු", "Duu": "ඪූ", "DU": "ඪූ",
"DR": "ඪෘ", "DRu": "ඪෲ",
"De": "ඪෙ", "Dee": "ඪේ", "DE": "ඪේ", "Dai": "ඪෛ",
"Do": "ඪො", "Doo": "ඪෝ", "DO": "ඪෝ", "Dau": "ඪෞ", "Dou": "ඪෞ",
"Dax": "ඪං", "Daz": "ඪං", "DaH": "ඪඃ",
"Dya": "ඪ්ය", "DYa": "ඪ්ය", "Dra": "ඪ්ර", "DRa": "ඪ්ර",
"DDa": "ඪ්ඪ",
"zD": "ඬ්", "zda": "ඬ", "zDaa": "ඬා", "zDA": "ඬැ", "zDAa": "ඬෑ", "zDAA": "ඬෑ",
"zDi": "ඬි", "zDii": "ඬී", "zDI": "ඬී", "zDu": "ඬු", "zDuu": "ඬූ", "zDU": "ඬූ",
"zDR": "ඬෘ", "zDRu": "ඬෲ",
"zDe": "ඬෙ", "zDee": "ඬේ", "zDE": "ඬේ", "zDai": "ඬෛ",
"zDo": "ඬො", "zDoo": "ඬෝ", "zDO": "ඬෝ", "zDau": "ඬෞ", "zDou": "ඬෞ",
"zDax": "ඬං", "zDaz": "ඬං", "zDaH": "ඬඃ",
"zDya": "ඬ්ය", "zDYa": "ඬ්ය", "zDra": "ඬ්ර", "zDRa": "ඬ්ර",
// THA group (dental) - all combinations
"th": "ත්", "tha": "ත", "thha": "ථ", "thaa": "තා", "thA": "තැ", "thAa": "තෑ", "thAA": "තෑ",
"thi": "ති", "thii": "තී", "thu": "තු", "thuu": "තූ", "thru": "තෘ", "the": "තෙ", "thee": "තේ",
"thai": "තෛ", "tho": "තො", "thoo": "තෝ", "thau": "තෞ", "thax": "තං", "thaz": "තං",
"thaH": "තඃ", "thya": "ත්ය", "thra": "ත්ර",
"Th": "ථ්", "Tha": "ථ", "Thaa": "ථා", "ThA": "ථැ", "ThAa": "ථෑ", "ThAA": "ථෑ",
"Thi": "ථි", "Thii": "ථී", "ThI": "ථී", "Thu": "ථු", "Thuu": "ථූ", "ThU": "ථූ",
"ThR": "ථෘ", "ThRu": "ථෲ",
"The": "ථෙ", "Thee": "ථේ", "ThE": "ථේ", "Thai": "ථෛ",
"Tho": "ථො", "Thoo": "ථෝ", "ThO": "ථෝ", "Thau": "ථෞ", "Thou": "ථෞ",
"Thax": "ථං", "Thaz": "ථං", "ThaH": "ථඃ",
"Thya": "ථ්ය", "ThYa": "ථ්ය", "Thra": "ථ්ර", "ThRa": "ථ්ර",
// DHA group (dental) - all combinations
"dh": "ද්", "dha": "ද", "dhha": "ධ", "dhaa": "දා", "dhA": "දැ", "dhAa": "දෑ", "dhAA": "දෑ",
"dhi": "දි", "dhii": "දී", "dhu": "දු", "dhuu": "දූ", "dhru": "දෘ", "dhe": "දෙ", "dhee": "දේ",
"dhai": "දෛ", "dho": "දො", "dhoo": "දෝ", "dhau": "දෞ", "dhax": "දං", "dhaz": "දං",
"dhaH": "දඃ", "dhya": "ද්ය", "dhra": "ද්ර",
"Dh": "ධ්", "Dha": "ධ", "Dhaa": "ධා", "DhA": "ධැ", "DhAa": "ධෑ", "DhAA": "ධෑ",
"Dhi": "ධි", "Dhii": "ධී", "DhI": "ධී", "Dhu": "ධු", "Dhuu": "ධූ", "DhU": "ධූ",
"DhR": "ධෘ", "DhRu": "ධෲ",
"Dhe": "ධෙ", "Dhee": "ධේ", "DhE": "ධේ", "Dhai": "ධෛ",
"Dho": "ධො", "Dhoo": "ධෝ", "DhO": "ධෝ", "Dhau": "ධෞ", "Dhou": "ධෞ",
"Dhax": "ධං", "Dhaz": "ධං", "DhaH": "ධඃ",
"Dhya": "ධ්ය", "DhYa": "ධ්ය", "Dra": "ධ්ර", "DhRa": "ධ්ර",
"zDh": "ඳ්", "zDha": "ඳ", "zDhaa": "ඳා", "zDhA": "ඳැ", "zDhAa": "ඳෑ", "zDhAA": "ඳෑ",
"zDhi": "ඳි", "zDhii": "ඳී", "zDhI": "ඳී", "zDhu": "ඳු", "zDhuu": "ඳූ", "zDhU": "ඳූ",
"zDhR": "ඳෘ", "zDhRu": "ඳෲ",
"zDhe": "ඳෙ", "zDhee": "ඳේ", "zDhE": "ඳේ", "zDhai": "ඳෛ",
"zDho": "ඳො", "zDhoo": "ඳෝ", "zDhO": "ඳෝ", "zDhau": "ඳෞ", "zDhou": "ඳෞ",
"zDhax": "ඳං", "zDhaz": "ඳං", "zDhaH": "ඳඃ",
"zDhya": "ඳ්ය", "zDhYa": "ඳ්ය", "zDhra": "ඳ්ර", "zDhRa": "ඳ්ර",
"qa": "ද", "q": "ද්", "zqa": "ඳ", "zq": "ඳ්",
// NA group - all combinations
"n": "න්", "na": "න", "Na": "ණ", "naa": "නා", "nA": "නැ", "nAa": "නෑ", "nAA": "නෑ",
"ni": "නි", "nii": "නී", "nu": "නු", "nuu": "නූ", "nru": "නෘ", "ne": "නෙ", "nee": "නේ",
"nai": "නෛ", "no": "නො", "noo": "නෝ", "nau": "නෞ", "nax": "නං", "naz": "නං",
"naH": "නඃ", "nya": "න්ය", "nra": "න්ර",
"N": "ණ්", "Na": "ණ", "Naa": "ණා", "NA": "ණැ", "NAa": "ණෑ", "NAA": "ණෑ",
"Ni": "ණි", "Nii": "ණී", "NI": "ණී", "Nu": "ණු", "Nuu": "ණූ", "NU": "ණූ",
"NR": "ණෘ", "NRu": "ණෲ",
"Ne": "ණෙ", "Nee": "ණේ", "NE": "ණේ", "Nai": "ණෛ",
"No": "ණො", "Noo": "ණෝ", "NO": "ණෝ", "Nau": "ණෞ", "Nou": "ණෞ",
"Nax": "ණං", "Naz": "ණං", "NaH": "ණඃ",
"Nya": "ණ්ය", "NYa": "ණ්ය", "Nra": "ණ්ර", "NRa": "ණ්ර",
"zna": "ඥ", "zN": "ඥ්", "zNa": "ඥ", "zha": "ඥ",
// PA group - all combinations
"p": "ප්", "pa": "ප", "pha": "ඵ", "paa": "පා", "pA": "පැ", "pAa": "පෑ", "pAA": "පෑ",
"pi": "පි", "pii": "පී", "pu": "පු", "puu": "පූ", "pru": "පෘ", "pe": "පෙ", "pee": "පේ",
"pai": "පෛ", "po": "පො", "poo": "පෝ", "pau": "පෞ", "pax": "පං", "paz": "පං",
"paH": "පඃ", "pya": "ප්ය", "pra": "ප්ර",
"praa": "ප්රා", "prA": "ප්රැ", "prAa": "ප්රෑ", "prAA": "ප්රෑ",
"pri": "ප්රි", "prii": "ප්රී", "pru": "ප්රු", "pruu": "ප්රූ",
"pre": "ප්රෙ", "pree": "ප්රේ", "prai": "ප්රෛ",
"pro": "ප්රො", "proo": "ප්රෝ", "prau": "ප්රෞ",
"ph": "ඵ්", "pha": "ඵ", "phaa": "ඵා", "phA": "ඵැ", "phAa": "ඵෑ", "phAA": "ඵෑ",
"phi": "ඵි", "phii": "ඵී", "phI": "ඵී", "phu": "ඵු", "phuu": "ඵූ", "phU": "ඵූ",
"phR": "ඵෘ", "phRu": "ඵෲ",
"phe": "ඵෙ", "phee": "ඵේ", "phE": "ඵේ", "phai": "ඵෛ",
"pho": "ඵො", "phoo": "ඵෝ", "phO": "ඵෝ", "phau": "ඵෞ", "phou": "ඵෞ",
"phax": "ඵං", "phaz": "ඵං", "phaH": "ඵඃ",
"phya": "ඵ්ය", "phYa": "ඵ්ය", "phra": "ඵ්ර", "phRa": "ඵ්ර",
"phraa": "ඵ්රා", "phraA": "ඵ්රැ", "phraAa": "ඵ්රෑ", "phraAA": "ඵ්රෑ",
"phri": "ඵ්රි", "phrii": "ඵ්රී", "phru": "ඵ්රු", "phruu": "ඵ්රූ",
"phre": "ඵ්රෙ", "phree": "ඵ්රේ", "phrai": "ඵ්රෛ",
"phro": "ඵ්රො", "phroo": "ඵ්රෝ", "phrau": "ඵ්රෞ",
"P": "ඵ්", "Pa": "ඵ", "Paa": "ඵා",
// BA group - all combinations
"b": "බ්", "ba": "බ", "bha": "භ", "baa": "බා", "bA": "බැ", "bAa": "බෑ", "bAA": "බෑ",
"bi": "බි", "bii": "බී", "bu": "බු", "buu": "බූ", "bru": "බෘ", "be": "බෙ", "bee": "බේ",
"bai": "බෛ", "bo": "බො", "boo": "බෝ", "bau": "බෞ", "bax": "බං", "baz": "බං",
"baH": "බඃ", "bya": "බ්ය", "bra": "බ්ර",
"bh": "භ්", "bha": "භ", "bhaa": "භා", "bhA": "භැ", "bhAa": "භෑ", "bhAA": "භෑ",
"bhi": "භි", "bhii": "භී", "bhI": "භී", "bhu": "භු", "bhuu": "භූ", "bhU": "භූ",
"bhR": "භෘ", "bhRu": "භෲ",
"bhe": "භෙ", "bhee": "භේ", "bhE": "භේ", "bhai": "භෛ",
"bho": "භො", "bhoo": "භෝ", "bhO": "භෝ", "bhau": "භෞ", "bhou": "භෞ",
"bhax": "භං", "bhaz": "භං", "bhaH": "භඃ",
"bhya": "භ්ය", "bhYa": "භ්ය", "bhra": "භ්ර", "bhRa": "භ්ර",
"B": "භ්", "Ba": "භ", "Baa": "භා",
"zB": "ඹ්", "zba": "ඹ", "zBaa": "ඹා", "zBA": "ඹැ", "zBAa": "ඹෑ", "zBAA": "ඹෑ",
"zBi": "ඹි", "zBii": "ඹී", "zBI": "ඹී", "zBu": "ඹු", "zBuu": "ඹූ", "zBU": "ඹූ",
"zBR": "ඹෘ", "zBRu": "ඹෲ",
"zBe": "ඹෙ", "zBee": "ඹේ", "zBE": "ඹේ", "zBai": "ඹෛ",
"zBo": "ඹො", "zBoo": "ඹෝ", "zBO": "ඹෝ", "zBau": "ඹෞ", "zBou": "ඹෞ",
"zBax": "ඹං", "zBaz": "ඹං", "zBaH": "ඹඃ",
"zBya": "ඹ්ය", "zBYa": "ඹ්ය", "zBra": "ඹ්ර", "zBRa": "ඹ්ර",
// MA group - all combinations
"m": "ම්", "ma": "ම", "maa": "මා", "mA": "මැ", "mAa": "මෑ", "mAA": "මෑ",
"mi": "මි", "mii": "මී", "mu": "මු", "muu": "මූ", "mru": "මෘ", "me": "මෙ", "mee": "මේ",
"mai": "මෛ", "mo": "මො", "moo": "මෝ", "mau": "මෞ", "max": "මං", "maz": "මං",
"maH": "මඃ", "mya": "ම්ය", "mra": "ම්ර",
// YA group - all combinations
"y": "ය්", "ya": "ය", "yaa": "යා", "yA": "යැ", "yAa": "යෑ", "yAA": "යෑ",
"yi": "යි", "yii": "යී", "yu": "යු", "yuu": "යූ", "yru": "යෘ", "ye": "යෙ", "yee": "යේ",
"yai": "යෛ", "yo": "යො", "yoo": "යෝ", "yau": "යෞ", "yax": "යං", "yaz": "යං",
"yaH": "යඃ", "yya": "ය්ය", "yra": "ය්ර",
// RA group - all combinations
"r": "ර්", "ra": "ර", "raa": "රා", "rA": "රැ", "rAa": "රෑ", "rAA": "රෑ",
"ri": "රි", "rii": "රී", "ru": "රු", "ruu": "රූ", "rru": "රෘ", "re": "රෙ", "ree": "රේ",
"rai": "රෛ", "ro": "රො", "roo": "රෝ", "rau": "රෞ", "rax": "රං", "raz": "රං",
"raH": "රඃ", "rya": "ර්ය", "rra": "ර්ර",
// LA group - all combinations
"l": "ල්", "la": "ල", "La": "ළ", "laa": "ලා", "lA": "ලැ", "lAa": "ලෑ", "lAA": "ලෑ",
"li": "ලි", "lii": "ලී", "lu": "ලු", "luu": "ලූ", "lru": "ලෘ", "le": "ලෙ", "lee": "ලේ",
"lai": "ලෛ", "lo": "ලො", "loo": "ලෝ", "lau": "ලෞ", "lax": "ලං", "laz": "ලං",
"laH": "ලඃ", "lya": "ල්ය", "lra": "ල්ර",
"L": "ළ්", "La": "ළ", "Laa": "ළා", "LA": "ළැ", "LAa": "ළෑ", "LAA": "ළෑ",
"Li": "ළි", "Lii": "ළී", "LI": "ළී", "Lu": "ළු", "Luu": "ළූ", "LU": "ළූ",
"LR": "ළෘ", "LRu": "ළෲ",
"Le": "ළෙ", "Lee": "ළේ", "LE": "ළේ", "Lai": "ළෛ",
"Lo": "ළො", "Loo": "ළෝ", "LO": "ළෝ", "Lau": "ළෞ", "Lou": "ළෞ",
"Lax": "ළං", "Laz": "ළං", "LaH": "ළඃ",
"Lya": "ළ්ය", "LYa": "ළ්ය", "Lra": "ළ්ර", "LRa": "ළ්ර",
// VA/WA group - all combinations
"v": "ව්", "w": "ව්", "va": "ව", "wa": "ව", "vaa": "වා", "waa": "වා",
"vA": "වැ", "wA": "වැ", "vAa": "වෑ", "wAa": "වෑ", "vAA": "වෑ", "wAA": "වෑ",
"vi": "වි", "wi": "වි", "vii": "වී", "wii": "වී", "vu": "වු", "wu": "වු",
"vuu": "වූ", "wuu": "වූ", "vru": "වෘ", "wru": "වෘ", "ve": "වෙ", "we": "වෙ",
"vee": "වේ", "wee": "වේ", "vai": "වෛ", "wai": "වෛ", "vo": "වො", "wo": "වො",
"voo": "වෝ", "woo": "වෝ", "vau": "වෞ", "wau": "වෞ", "vax": "වං", "wax": "වං",
"vaz": "වං", "waz": "වං", "vaH": "වඃ", "waH": "වඃ", "vya": "ව්ය", "wya": "ව්ය",
"vra": "ව්ර", "wra": "ව්ර",
// SA group - all combinations
"s": "ස්", "sa": "ස", "sha": "ශ", "Sa": "ෂ", "Sha": "ෂ",
"saa": "සා", "sA": "සැ", "sAa": "සෑ", "sAA": "සෑ",
"si": "සි", "sii": "සී", "su": "සු", "suu": "සූ", "sru": "සෘ", "se": "සෙ", "see": "සේ",
"sai": "සෛ", "so": "සො", "soo": "සෝ", "sau": "සෞ", "sax": "සං", "saz": "සං",
"saH": "සඃ", "sya": "ස්ය", "sra": "ස්ර", "sh": "ශ්", "S": "ෂ්", "Sh": "ෂ්",
"sha": "ශ", "shaa": "ශා", "shA": "ශැ", "shAa": "ශෑ", "shAA": "ශෑ",
"shi": "ශි", "shii": "ශී", "shu": "ශු", "shuu": "ශූ", "shru": "ශෘ", "she": "ශෙ", "shee": "ශේ",
"shai": "ශෛ", "sho": "ශො", "shoo": "ශෝ", "shau": "ශෞ", "shax": "ශං", "shaz": "ශං",
"shaH": "ශඃ", "shya": "ශ්ය", "shra": "ශ්ර",
"Sa": "ෂ", "Saa": "ෂා", "SA": "ෂැ", "SAa": "ෂෑ", "SAA": "ෂෑ",
"Si": "ෂි", "Sii": "ෂී", "Su": "ෂු", "Suu": "ෂූ", "Sru": "ෂෘ", "Se": "ෂෙ", "See": "ෂේ",
"Sai": "ෂෛ", "So": "ෂො", "Soo": "ෂෝ", "Sau": "ෂෞ", "Sax": "ෂං", "Saz": "ෂං",
"SaH": "ෂඃ", "Sya": "ෂ්ය", "Sra": "ෂ්ර",
// HA group - all combinations
"h": "හ්", "ha": "හ", "haa": "හා", "hA": "හැ", "hAa": "හෑ", "hAA": "හෑ",
"hi": "හි", "hii": "හී", "hu": "හු", "huu": "හූ", "hru": "හෘ", "he": "හෙ", "hee": "හේ",
"hai": "හෛ", "ho": "හො", "hoo": "හෝ", "hau": "හෞ", "hax": "හං", "haz": "හං",
"haH": "හඃ", "hya": "හ්ය", "hra": "හ්ර",
// FA group - all combinations
"f": "ෆ්", "fa": "ෆ", "faa": "ෆා", "fA": "ෆැ", "fAa": "ෆෑ", "fAA": "ෆෑ",
"fi": "ෆි", "fii": "ෆී", "fu": "ෆු", "fuu": "ෆූ", "fru": "ෆෘ", "fe": "ෆෙ", "fee": "ෆේ",
"fai": "ෆෛ", "fo": "ෆො", "foo": "ෆෝ", "fau": "ෆෞ", "fax": "ෆං", "faz": "ෆං",
"faH": "ෆඃ", "fya": "ෆ්ය", "fra": "ෆ්ර",
// Special consonant combinations
"nda": "න්ද", "ndha": "න්ධ", "ndra": "න්ද්ර", "ndhra": "න්ධ්ර", "nsa": "න්ස",
"mba": "ම්බ", "mbha": "ම්භ", "mpa": "ම්ප", "mpha": "ම්ඵ",
"lla": "ල්ල", "ksha": "ක්ෂ", "kSa": "ක්ෂ", "gna": "ඥ", "jna": "ඥ",
// Nasalized consonants
"zka": "ඤ", "zKa": "ඤ", "zk": "ඤ්", "zK": "ඤ්",
"nga": "ඞ්ග", "nka": "ඞ්ක",
// Additional common combinations and special forms
"kSha": "ක්ෂ", "kS": "ක්ෂ්", "kSh": "ක්ෂ්", "kSa": "ක්ෂ", "GYa": "ඥ",
// Additional "ra" combinations for various consonants
"jra": "ජ්ර", "jraa": "ජ්රා", "jrA": "ජ්රැ", "jrAa": "ජ්රෑ", "jrAA": "ජ්රෑ",
"jri": "ජ්රි", "jrii": "ජ්රී", "jrI": "ජ්රී", "jru": "ජ්රු", "jruu": "ජ්රූ", "jrU": "ජ්රූ",
"jre": "ජ්රෙ", "jree": "ජ්රේ", "jrE": "ජ්රේ", "jrai": "ජ්රෛ",
"jro": "ජ්රො", "jroo": "ජ්රෝ", "jrO": "ජ්රෝ", "jrau": "ජ්රෞ", "jrou": "ජ්රෞ",
"tra": "ට්ර", "traa": "ට්රා", "trA": "ට්රැ", "trAa": "ට්රෑ", "trAA": "ට්රෑ",
"tri": "ට්රි", "trii": "ට්රී", "trI": "ට්රී", "tru": "ට්රු", "truu": "ට්රූ", "trU": "ට්රූ",
"tre": "ට්රෙ", "tree": "ට්රේ", "trE": "ට්රේ", "trai": "ට්රෛ",
"tro": "ට්රො", "troo": "ට්රෝ", "trO": "ට්රෝ", "trau": "ට්රෞ", "trou": "ට්රෞ",
"dra": "ඩ්ර", "draa": "ඩ්රා", "drA": "ඩ්රැ", "drAa": "ඩ්රෑ", "drAA": "ඩ්රෑ",
"dri": "ඩ්රි", "drii": "ඩ්රී", "drI": "ඩ්රී", "dru": "ඩ්රු", "druu": "ඩ්රූ", "drU": "ඩ්රූ",
"dre": "ඩ්රෙ", "dree": "ඩ්රේ", "drE": "ඩ්රේ", "drai": "ඩ්රෛ",
"dro": "ඩ්රො", "droo": "ඩ්රෝ", "drO": "ඩ්රෝ", "drau": "ඩ්රෞ", "drou": "ඩ්රෞ",
"nra": "න්ර", "nraa": "න්රා", "nrA": "න්රැ", "nrAa": "න්රෑ", "nrAA": "න්රෑ",
"nri": "න්රි", "nrii": "න්රී", "nrI": "න්රී", "nru": "න්රු", "nruu": "න්රූ", "nrU": "න්රූ",
"nre": "න්රෙ", "nree": "න්රේ", "nrE": "න්රේ", "nrai": "න්රෛ",
"nro": "න්රො", "nroo": "න්රෝ", "nrO": "න්රෝ", "nrau": "න්රෞ", "nrou": "න්රෞ",
"bra": "බ්ර", "braa": "බ්රා", "brA": "බ්රැ", "brAa": "බ්රෑ", "brAA": "බ්රෑ",
"bri": "බ්රි", "brii": "බ්රී", "brI": "බ්රී", "bru": "බ්රු", "bruu": "බ්රූ", "brU": "බ්රූ",
"bre": "බ්රෙ", "bree": "බ්රේ", "brE": "බ්රේ", "brai": "බ්රෛ",
"bro": "බ්රො", "broo": "බ්රෝ", "brO": "බ්රෝ", "brau": "බ්රෞ", "brou": "බ්රෞ",
"mra": "ම්ර", "mraa": "ම්රා", "mrA": "ම්රැ", "mrAa": "ම්රෑ", "mrAA": "ම්රෑ",
"mri": "ම්රි", "mrii": "ම්රී", "mrI": "ම්රී", "mru": "ම්රු", "mruu": "ම්රූ", "mrU": "ම්රූ",
"mre": "ම්රෙ", "mree": "ම්රේ", "mrE": "ම්රේ", "mrai": "ම්රෛ",
"mro": "ම්රො", "mroo": "ම්රෝ", "mrO": "ම්රෝ", "mrau": "ම්රෞ", "mrou": "ම්රෞ",
"yra": "ය්ර", "yraa": "ය්රා", "yrA": "ය්රැ", "yrAa": "ය්රෑ", "yrAA": "ය්රෑ",
"yri": "ය්රි", "yrii": "ය්රී", "yrI": "ය්රී", "yru": "ය්රු", "yruu": "ය්රූ", "yrU": "ය්රූ",
"yre": "ය්රෙ", "yree": "ය්රේ", "yrE": "ය්රේ", "yrai": "ය්රෛ",
"yro": "ය්රො", "yroo": "ය්රෝ", "yrO": "ය්රෝ", "yrau": "ය්රෞ", "yrou": "ය්රෞ",
"rra": "ර්ර", "rraa": "ර්රා", "rrA": "ර්රැ", "rrAa": "ර්රෑ", "rrAA": "ර්රෑ",
"rri": "ර්රි", "rrii": "ර්රී", "rrI": "ර්රී", "rru": "ර්රු", "rruu": "ර්රූ", "rrU": "ර්රූ",
"rre": "ර්රෙ", "rree": "ර්රේ", "rrE": "ර්රේ", "rrai": "ර්රෛ",
"rro": "ර්රො", "rroo": "ර්රෝ", "rrO": "ර්රෝ", "rrau": "ර්රෞ", "rrou": "ර්රෞ",
"lra": "ල්ර", "lraa": "ල්රා", "lrA": "ල්රැ", "lrAa": "ල්රෑ", "lrAA": "ල්රෑ",
"lri": "ල්රි", "lrii": "ල්රී", "lrI": "ල්රී", "lru": "ල්රු", "lruu": "ල්රූ", "lrU": "ල්රූ",
"lre": "ල්රෙ", "lree": "ල්රේ", "lrE": "ල්රේ", "lrai": "ල්රෛ",
"lro": "ල්රො", "lroo": "ල්රෝ", "lrO": "ල්රෝ", "lrau": "ල්රෞ", "lrou": "ල්රෞ",
"wra": "ව්ර", "vra": "ව්ර", "wraa": "ව්රා", "vraa": "ව්රා",
"wrA": "ව්රැ", "vrA": "ව්රැ", "wrAa": "ව්රෑ", "vrAa": "ව්රෑ", "wrAA": "ව්රෑ", "vrAA": "ව්රෑ",
"wri": "ව්රි", "vri": "ව්රි", "wrii": "ව්රී", "vrii": "ව්රී",
"wrI": "ව්රී", "vrI": "ව්රී", "wru": "ව්රු", "vru": "ව්රු",
"wruu": "ව්රූ", "vruu": "ව්රූ", "wrU": "ව්රූ", "vrU": "ව්රූ",
"wre": "ව්රෙ", "vre": "ව්රෙ", "wree": "ව්රේ", "vree": "ව්රේ",
"wrE": "ව්රේ", "vrE": "ව්රේ", "wrai": "ව්රෛ", "vrai": "ව්රෛ",
"wro": "ව්රො", "vro": "ව්රො", "wroo": "ව්රෝ", "vroo": "ව්රෝ",
"wrO": "ව්රෝ", "vrO": "ව්රෝ", "wrau": "ව්රෞ", "vrau": "ව්රෞ",
"wrou": "ව්රෞ", "vrou": "ව්රෞ",
"Sra": "ෂ්ර", "Sraa": "ෂ්රා", "SrA": "ෂ්රැ", "SrAa": "ෂ්රෑ", "SrAA": "ෂ්රෑ",
"Sri": "ෂ්රි", "Srii": "ෂ්රී", "SrI": "ෂ්රී", "Sru": "ෂ්රු", "Sruu": "ෂ්රූ", "SrU": "ෂ්රූ",
"Sre": "ෂ්රෙ", "Sree": "ෂ්රේ", "SrE": "ෂ්රේ", "Srai": "ෂ්රෛ",
"Sro": "ෂ්රො", "Sroo": "ෂ්රෝ", "SrO": "ෂ්රෝ", "Srau": "ෂ්රෞ", "Srou": "ෂ්රෞ",
"shra": "ශ්ර", "shraa": "ශ්රා", "shrA": "ශ්රැ", "shrAa": "ශ්රෑ", "shrAA": "ශ්රෑ",
"shri": "ශ්රි", "shrii": "ශ්රී", "shrI": "ශ්රී", "shru": "ශ්රු", "shruu": "ශ්රූ", "shrU": "ශ්රූ",
"shre": "ශ්රෙ", "shree": "ශ්රේ", "shrE": "ශ්රේ", "shrai": "ශ්රෛ",
"shro": "ශ්රො", "shroo": "ශ්රෝ", "shrO": "ශ්රෝ", "shrau": "ශ්රෞ", "shrou": "ශ්රෞ",
"hra": "හ්ර", "hraa": "හ්රා", "hrA": "හ්රැ", "hrAa": "හ්රෑ", "hrAA": "හ්රෑ",
"hri": "හ්රි", "hrii": "හ්රී", "hrI": "හ්රී", "hru": "හ්රු", "hruu": "හ්රූ", "hrU": "හ්රූ",
"hre": "හ්රෙ", "hree": "හ්රේ", "hrE": "හ්රේ", "hrai": "හ්රෛ",
"hro": "හ්රො", "hroo": "හ්රෝ", "hrO": "හ්රෝ", "hrau": "හ්රෞ", "hrou": "හ්රෞ",
"fra": "ෆ්ර", "fraa": "ෆ්රා", "frA": "ෆ්රැ", "frAa": "ෆ්රෑ", "frAA": "ෆ්රෑ",
"fri": "ෆ්රි", "frii": "ෆ්රී", "frI": "ෆ්රී", "fru": "ෆ්රු", "fruu": "ෆ්රූ", "frU": "ෆ්රූ",
"fre": "ෆ්රෙ", "free": "ෆ්රේ", "frE": "ෆ්රේ", "frai": "ෆ්රෛ",
"fro": "ෆ්රො", "froo": "ෆ්රෝ", "frO": "ෆ්රෝ", "frau": "ෆ්රෞ", "frou": "ෆ්රෞ",
};
// Vowel modifiers that can be applied after a consonant
const vowelModifiers = {
"a": "", // No change (remove hal)
"A": "ැ", // æ
"aa": "ා", // ā
"AA": "ෑ", // ǣ
"Aa": "ෑ", // ǣ (alternate)
"i": "ි", // i
"ii": "ී", // ī
"I": "ී", // ī (alternate)
"u": "ු", // u
"uu": "ූ", // ū
"U": "ූ", // ū (alternate)
"e": "ෙ", // e
"ee": "ේ", // ē
"E": "ේ", // ē (alternate)
"ai": "ෛ", // ai
"o": "ො", // o
"oo": "ෝ", // ō
"O": "ෝ", // ō (alternate)
"au": "ෞ", // au
"ou": "ෞ", // au (alternate)
"ru": "ෘ", // ṛ
"Ru": "ෲ", // ṝ
"ax": "ං", // aṁ
"az": "ං", // aṁ (alternate)
"aH": "ඃ", // aḥ
};
// Special modifiers that can be applied after a consonant
const specialModifiers = {
"ya": "්ය", // consonant + ya (ZWNJ in middle)
"ra": "්ර", // consonant + ra (ZWNJ in middle)
"Y": "්ය", // alternate
"R": "්ර" // alternate
};
// Initialize state variables
let sinhalaInputEnabled = false;
let keyBuffer = []; // Array of individual keypresses
let typingTimer = null;
// Get typing delay from window or localStorage, with fallback
function getTypingDelay() {
if (typeof window !== 'undefined' && window.SINHALA_TYPING_DELAY) {
return window.SINHALA_TYPING_DELAY;
}
if (typeof localStorage !== 'undefined') {
const speed = localStorage.getItem('sinhalaTypingSpeed') || 'moderate'; // Default to moderate
switch (speed) {
case 'fast': return 250;
case 'moderate': return 500;
case 'slow': return 750;
default: return 500;
}
}
return 500; // Default fallback
}
// Track elements that already have listeners to prevent duplicates
const elementsWithListeners = new WeakSet();
// Prepare consonant bases for easier lookup
function initializeConsonantBases() {
// Automatically extract base forms from the singleKeyMap entries with hal
for (const key in sinhalaPatternMap) {
const value = sinhalaPatternMap[key];
if (value && value.endsWith('්')) {
value.slice(0, -1); // Remove hal
}
}
}
/**
* Initialize the Sinhala input functionality
* @param {boolean} startEnabled - Whether to start with the feature enabled
*/
function initializeSinhalaInput(startEnabled = false) {
console.log("Initializing Sinhala input functionality...");
// Initialize typing speed from localStorage
if (typeof window !== 'undefined') {
const savedSpeed = localStorage.getItem('sinhalaTypingSpeed') || 'moderate'; // Default to moderate
const typingDelay = getTypingDelayFromSpeed$1(savedSpeed);
window.SINHALA_TYPING_DELAY = typingDelay;
console.log("Initial typing delay:", typingDelay, "ms");
}
// Initialize consonant bases
initializeConsonantBases();
// Set initial state
sinhalaInputEnabled = startEnabled;
localStorage.setItem('sinhalaInputEnabled', startEnabled.toString());
console.log("Initial Sinhala input state:", sinhalaInputEnabled);
// Setup the toggle
const toggle = document.getElementById('sinhalaToggle');
if (toggle) {
toggle.checked = sinhalaInputEnabled;
toggle.addEventListener('change', function() {
sinhalaInputEnabled = this.checked;
localStorage.setItem('sinhalaInputEnabled', sinhalaInputEnabled);
console.log('Sinhala keyboard ' + (sinhalaInputEnabled ? 'enabled' : 'disabled'));
// Reset state when toggling
resetState();
});
}
// Set up event listeners
setupKeyboardMapping();
}
// Helper to convert speed setting to ms
function getTypingDelayFromSpeed$1(speed) {
switch (speed) {
case 'fast': return 250;
case 'moderate': return 500;
case 'slow': return 750;
default: return 500; // Default to moderate
}
}
// Reset state with proper cleanup
function resetState() {
if (typingTimer) {
clearTimeout(typingTimer);
typingTimer = null;
}
keyBuffer = [];
}
// Setup real-time keyboard mapping
function setupKeyboardMapping() {
console.log("Setting up keyboard mapping event listeners");
// Attach direct listeners to all input elements without duplicating
document.querySelectorAll('input[type="text"], input[type="search"], textarea, [contenteditable="true"]').forEach(element => {
attachKeyListenerIfNeeded(element);
});
// Watch for new input elements using MutationObserver
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.addedNodes.length) {
mutation.addedNodes.forEach((node) => {
if (node.nodeName === 'INPUT' || node.nodeName === 'TEXTAREA' ||
(node.getAttribute && node.getAttribute('contenteditable') === 'true')) {
attachKeyListenerIfNeeded(node);
}
// Check for descendants that might be input elements
if (node.querySelectorAll) {
node.querySelectorAll('input[type="text"], input[type="search"], textarea, [contenteditable="true"]')
.forEach(element => {
attachKeyListenerIfNeeded(element);
});
}
});
}
});
});
// Start observing the document
observer.observe(document.body, { childList: true, subtree: true });
// Handle focus to reset state
document.addEventListener('click', function(e) {
resetState();
});
}
// Helper to avoid adding duplicate listeners
function attachKeyListenerIfNeeded(element) {
if (!elementsWithListeners.has(element)) {
element.addEventListener('keydown', handleKeyEvent, true);
elementsWithListeners.add(element);
}
}
// Modified function to handle real-time typing with proper buffering
function handleKeyEvent(e) {
if (!sinhalaInputEnabled) return;
// Only process in input fields
const target = e.target;
if (
!(target.tagName === 'INPUT' && (target.type === 'text' || target.type === 'search')) &&
target.tagName !== 'TEXTAREA' &&
target.contentEditable !== 'true'
) {
return;
}
// Handle special keys
if (e.key === 'Backspace') {
// Clear typing timer if active
if (typingTimer) {
clearTimeout(typingTimer);
typingTimer = null;
}
// If we have characters in buffer, remove the last one
if (keyBuffer.length > 0) {
keyBuffer.pop();
e.preventDefault();
return false;
}
return;
}
// Handle other control keys - process immediately
if (e.key === 'Delete' ||
e.key === 'Enter' || e.key === 'Tab' ||
e.key === 'ArrowLeft' || e.key === 'ArrowRight' ||
e.key === 'ArrowUp' || e.key === 'ArrowDown' ||
e.ctrlKey || e.metaKey || e.altKey) {
// Process any pending characters immediately
if (typingTimer) {
clearTimeout(typingTimer);
typingTimer = null;
}
if (keyBuffer.length > 0) {
processBufferWithOptimizedMatching(target);
}
// Reset state on certain keys
if (e.key === 'Enter') {
resetState();
}
return;
}
// Space is a delimiter - process buffer then add space
if (e.key === ' ') {
if (typingTimer) {
clearTimeout(typingTimer);
typingTimer = null;
}
if (keyBuffer.length > 0) {
processBufferWithOptimizedMatching(target);
}
// Insert the space
insertCharacter(target, ' ');
resetState();
e.preventDefault();
return false;
}
// Only handle single characters with latency
if (e.key.length === 1) {
// Add to key buffer as individual character
keyBuffer.push(e.key);
e.preventDefault();
// Clear any existing timer
if (typingTimer) {
clearTimeout(typingTimer);
}
// Set timer to process after delay
typingTimer = setTimeout(() => {
processBufferWithOptimizedMatching(target);
typingTimer = null;
}, getTypingDelay());
return false;
}
}
// Optimized processing function with 5-character maximum pattern length
function processBufferWithOptimizedMatching(target) {
if (keyBuffer.length === 0) return;
keyBuffer.join('');
let result = '';
let position = 0;
while (position < keyBuffer.length) {
let longestMatch = null;
let longestMatchLength = 0;
let longestMatchValue = null;
// Try finding the longest match starting at the current position
// Limit to maximum 5 characters since no pattern exceeds this length
const maxPatternLength = Math.min(5, keyBuffer.length - position);
for (let patternLength = maxPatternLength; patternLength > 0; patternLength--) {
const slice = keyBuffer.slice(position, position + patternLength).join('');
if (sinhalaPatternMap[slice]) {
longestMatch = slice;
longestMatchLength = patternLength;
longestMatchValue = sinhalaPatternMap[slice];
break; // Found the longest match, stop searching
}
}
// If no direct match found, check for consonant + vowel combinations
if (!longestMatch && position < keyBuffer.length - 1) {
const consonantKey = keyBuffer[position];
if (isConsonantWithHal(consonantKey)) {
// Try to find the longest vowel modifier after this consonant
// Limit to maximum 4 characters for vowel modifiers (since longest consonant+vowel could be 5)
const maxVowelLength = Math.min(4, keyBuffer.length - position - 1);
for (let vowelLength = maxVowelLength; vowelLength > 0; vowelLength--) {
const vowelModifier = keyBuffer.slice(position + 1, position + 1 + vowelLength).join('');
if (vowelModifiers[vowelModifier]) {
const consonantWithHal = sinhalaPatternMap[consonantKey];
const base = getConsonantBase(consonantWithHal);
const modifier = vowelModifiers[vowelModifier];
longestMatch = consonantKey + vowelModifier;
longestMatchLength = 1 + vowelLength;
longestMatchValue = base + modifier;
break;
}
}
}
}
// Special case for consonant + special modifier (ra, ya)
if (!longestMatch && position < keyBuffer.length - 1) {
const consonantKey = keyBuffer[position];
if (isConsonantWithHal(consonantKey)) {
const maxSpecialLength = Math.min(2, keyBuffer.length - position - 1);
for (let specialLength = maxSpecialLength; specialLength > 0; specialLength--) {
const specialModifier = keyBuffer.slice(position + 1, position + 1 + specialLength).join('');
if (specialModifiers[specialModifier]) {
const consonantWithHal = sinhalaPatternMap[consonantKey];
const base = getConsonantBase(consonantWithHal);
const modifier = specialModifiers[specialModifier];
longestMatch = consonantKey + specialModifier;
longestMatchLength = 1 + specialLength;
longestMatchValue = base + modifier;
break;
}
}
}
}
if (longestMatch) {
// Add the matched character to the result
result += longestMatchValue;
position += longestMatchLength;
} else {
// No match found, add the current character as-is
result += keyBuffer[position];
position++;
}
}
// Insert the processed result
if (result) {
insertCharacter(target, result);
}
// Clear the buffer
keyBuffer = [];
}
// Helper function to check if a character represents a consonant with hal
function isConsonantWithHal(char) {
const mappedChar = sinhalaPatternMap[char];
return mappedChar && mappedChar.endsWith('්');
}
// Helper function to get consonant base (remove hal)
function getConsonantBase(consonantWithHal) {
if (consonantWithHal && consonantWithHal.endsWith('්')) {
return consonantWithHal.slice(0, -1);
}
return consonantWithHal;
}
// Insert a character at the current position
function insertCharacter(target, char) {
if (target.contentEditable === 'true') {
// For contenteditable elements
insertIntoContentEditable(target, char);
} else {
// For input and textarea elements
insertIntoInput(target, char);
}
}
// Insert text into a contenteditable element
function insertIntoContentEditable(target, text) {
const selection = window.getSelection();
const range = selection.getRangeAt(0);
// Delete any selected text
range.deleteContents();
// Insert the new text
const textNode = document.createTextNode(text);
range.insertNode(textNode);
// Move cursor to the end
selection.removeAllRanges();
const newRange = document.createRange();
newRange.setStartAfter(textNode);
newRange.collapse(true);
selection.addRange(newRange);
}
// Insert text into an input or textarea
function insertIntoInput(target, text) {
const start = target.selectionStart;
const end = target.selectionEnd;
const value = target.value;
// Replace selected text or insert at cursor
target.value = value.substring(0, start) + text + value.substring(end);
// Move cursor to the right position
const newPosition = start + text.length;
target.selectionStart = newPosition;
target.selectionEnd = newPosition;
}
// Main entry point for the Singlish to Sinhala converter package
/**
* Initialize the Singlish to Sinhala converter
* @param {Object} options - Configuration options
* @param {boolean} options.autoAddToggle - Automatically add the toggle button (default: true)
* @param {string} options.togglePosition - Position of toggle ('bottom-right', 'bottom-left', 'top-right', 'top-left') (default: 'bottom-right')
* @param {boolean} options.startEnabled - Start with conversion enabled (default: false)
* @returns {Object} - API object with control methods
*/
function initSinglishToSinhala(options = {}) {
const {
autoAddToggle = true,
togglePosition = 'bottom-right',
startEnabled = false
} = options;
// Add toggle if requested
if (autoAddToggle) {
addToggleToDOM(togglePosition, startEnabled);
}
// Initialize the input processor
initializeSinhalaInput(startEnabled);
// Return API object
return {
enable: () => {
const toggle = document.getElementById('sinhalaToggle');
if (toggle) {
toggle.checked = true;
toggle.dispatchEvent(new Event('change'));
}
localStorage.setItem('sinhalaInputEnabled', 'true');
},
disable: () => {
const toggle = document.getElementById('sinhalaToggle');
if (toggle) {
toggle.checked = false;
toggle.dispatchEvent(new Event('change'));
}
localStorage.setItem('sinhalaInputEnabled', 'false');
resetState();
},
isEnabled: () => {
return localStorage.getItem('sinhalaInputEnabled') === 'true';
},
setTypingSpeed: (speed) => {
// Valid values: 'slow', 'moderate', 'fast'
if (['slow', 'moderate', 'fast'].includes(speed)) {
localStorage.setItem('sinhalaTypingSpeed', speed);
const typingDelay = getTypingDelayFromSpeed(speed);
updateTypingDelay(typingDelay);
// Update slider if it exists
const speedSlider = document.getElementById('speedSlider');
if (speedSlider) {
let sliderVal = 1; // Default to moderate
if (speed === 'slow') sliderVal = 0;
else if (speed === 'fast') sliderVal = 2;
speedSlider.value = sliderVal;
updateSpeedLabels(speed);
}
}
},
getTypingSpeed: () => {
return localStorage.getItem('sinhalaTypingSpeed') || 'moderate';
},
toggle: () => {
const toggle = document.getElementById('sinhalaToggle');
if (toggle) {
toggle.checked = !toggle.checked;
toggle.dispatchEvent(new Event('change'));
}
}
};
}
// Create and add toggle to DOM
function addToggleToDOM(position = 'bottom-right', startEnabled = false) {
// Remove existing toggle if present
const existingToggle = document.getElementById('sinhala-toggle-container');
if (existingToggle) {
existingToggle.parentNode.removeChild(existingToggle);
}
// Create container
const container = document.createElement('div');
container.id = 'sinhala-toggle-container';
container.className = 'sinhala-toggle-container';
// Position the toggle
switch (position) {
case 'top-left':
container.style.top = '20px';
container.style.left = '20px';
container.style.bottom = 'auto';
container.style.right = 'auto';
break;
case 'top-right':
container.style.top = '20px';
container.style.right = '20px';
container.style.bottom = 'auto';
container.style.left = 'auto';
break;
case 'bottom-left':
container.style.bottom = '20px';
container.style.left = '20px';
container.style.top = 'auto';
container.style.right = 'auto';
break;
case 'bottom-right':
default:
container.style.bottom = '20px';
container.style.right = '20px';
container.style.top = 'auto';
container.style.left = 'auto';
}
// Create toggle content
container.innerHTML = `
<div class="toggle-label">සිංහල</div>
<label class="switch">
<input type="checkbox" id="sinhalaToggle" ${startEnabled ? 'checked' : ''}>
<span class="slider round"></span>
</label>
<div class="settings-icon" id="sinhalaSettings">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"></circle>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
</svg>
</div>
<div class="speed-settings-popup" id="speedSettingsPopup">
<div class="speed-settings-content">
<h4>Typing Speed</h4>
<div class="speed-slider-container">
<div class="speed-labels">
<span class="speed-label" data-speed="slow">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12l4.58-4.59z"/></svg>
Slow
</span>
<span class="speed-label" data-speed="moderate">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
Moderate
</span>
<span class="speed-label" data-speed="fast">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9.99999 6L8.58999 7.41L13.17 12l-4.58001 4.59L9.99999 18l6-6L9.99999 6z"/></svg>
Fast
</span>
</div>
<input type="range" min="0" max="2" value="1" step="1" id="speedSlider" class="speed-slider">
</div>
<div class="speed-description">
<span id="speedDescription">Moderate Typing Speed</span>
</div>
</div>
</div>
`;
// Append to document body
document.body.appendChild(container);
// Set up the settings icon and popup
const settingsIcon = document.getElementById('sinhalaSettings');
const settingsPopup = document.getElementById('speedSettingsPopup');
const speedSlider = document.getElementById('speedSlider');
// Initialize slider to saved value or default
const savedSpeed = localStorage.getItem('sinhalaTypingSpeed') || 'moderate';
let initialSliderValue = 1; // Default to moderate (slider value 1)
if (savedSpeed === 'slow') {
initialSliderValue = 0;
} else if (savedSpeed === 'fast') {
initialSliderValue = 2;
}
speedSlider.value = initialSliderValue;
updateSpeedLabels(savedSpeed);
// Toggle popup when clicking the settings icon
settingsIcon.addEventListener('click', (e) => {
e.stopPropagation();
settingsPopup.classList.toggle('active');
});
// Close popup when clicking outside
document.addEventListener('click', (e) => {
if (settingsPopup.classList.contains('active') &&
!settingsPopup.contains(e.target) &&
e.target !== settingsIcon) {
settingsPopup.classList.remove('active');
}
});
// Prevent closing when clicking inside the popup
settingsPopup.addEventListener('click', (e) => {
e.stopPropagation();
});
// Handle slider changes
document.querySelectorAll('.speed-label').forEach(label => {
label.addEventListener('click', function() {
const speed = this.getAttribute('data-speed');
if (speed === 'slow') speedSlider.value = 0;
else if (speed === 'moderate') speedSlider.value = 1;
else if (speed === 'fast') speedSlider.value = 2;
speedSlider.dispatchEvent(new Event('input'));
});
});
speedSlider.addEventListener('input', function() {
const value = parseInt(this.value, 10);
let speed;
switch (value) {
case 0: speed = 'slow'; break;
case 1: speed = 'moderate'; break;
case 2: speed = 'fast'; break;
default: speed = 'moderate';
}
localStorage.setItem('sinhalaTypingSpeed', speed);
updateSpeedLabels(speed);
// Update the typing delay
const typingDelay = getTypingDelayFromSpeed(speed);
updateTypingDelay(typingDelay);
});
// Helper function to update visual state of speed labels
function updateSpeedLabels(speed) {
const speedLabels = document.querySelectorAll('.speed-label');
speedLabels.forEach(label => {
if (label.getAttribute('data-speed') === speed) {
label.classList.add('active');
} else {
label.classList.remove('active');
}
});
const speedDescription = document.getElementById('speedDescription');
switch (speed) {
case 'slow':
speedDescription.textContent = 'Slow Typing Speed';
break;
case 'moderate':
speedDescription.textContent = 'Moderate Typing Speed';
break;
case 'fast':
speedDescription.textContent = 'Fast Typing Speed';
break;
default:
speedDescription.textContent = 'Moderate Typing Speed';
break;
}
}
// Helper function to update typing delay
function updateTypingDelay(delayMs) {
if (typeof window !== 'undefined' && window.singlishToSinhala) {
window.SINHALA_TYPING_DELAY = delayMs;
}
}
// Helper to convert speed setting to ms
function getTypingDelayFromSpeed(speed) {
switch (speed) {
case 'fast': return 250;
case 'moderate': return 500;
case 'slow': return 750;
default: return 500; // Default to moderate
}
}
}
// Auto-initialize if window is available and autoInit is not disabled
if (typeof window !== 'undefined' && !window.SINGLISH_NO_AUTO_INIT) {
// Use DOM ready check before auto-initializing
function initOnReady() {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
window.singlishTo