Skip to content

Instantly share code, notes, and snippets.

@derekmc
Last active March 5, 2025 22:17
Show Gist options
  • Save derekmc/9f6c4fd784de9296183bf972889a607b to your computer and use it in GitHub Desktop.
Save derekmc/9f6c4fd784de9296183bf972889a607b to your computer and use it in GitHub Desktop.
A basic editor for the "simple chord" chorded keyboard.
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/* CSS */
/* The switch - the box around the slider */
h1, h2, h3, h4{
display: inline-block;
}
.h4{
font-weight: bold;
font-size: 18px;
height: 200px;
margin: 5px 10px;
}
.checkgroup{
white-space: nowrap;
}
button{
border: 2px solid #888;
background: #ccc;
font-size: 14px;
padding: 3px 10px;
font-weight: bold;
}
body{
font-family: sans-serif;
overflow: hidden;
user-select:none;
margin: 8px;
margin-top: 20px;
}
label{
cursor: pointer;
}
#textout::selection{
background: #fff;
color: #008;
}
#textout::-webkit-scrollbar{
display: none;
}
#textout:focus{
border-top: 3pxss #bdf;
border-bottom: 3px solid #bdf;
outline: none;
}
#textout{
margin-top: 10px;
border: none;
border-top: 3px solid #bdf;
border-bottom: 3px solid #bdf;
background: #46f;
color: #fff;
padding: 6px calc(2vw + 8px);
margin-left: -9px;
margin-right: -12px;
width: calc(100vw + 8px);
box-sizing: border-box;
height: calc(98vh - 90px);
}
@media only screen and (max-width: 630px) {
#textout{
height: calc(98vh - 140px);
}
.controls, #typingstatus{
margin-top: 10px;
}
}
@media only screen and (max-width: 400px) {
#textout{
height: calc(98vh - 200px);
}
}
:root{
--switch-width: 31px;
--switch-height: 19px;
--switch-slide: 13px;
--switch-slide-neg: -13px;
--switch-inner: 13px;
}
var
body{
font-family: sans-serif;
}
.switch {
position: relative;
display: inline-block;
width: var(--switch-width);
height: var(--switch-height);
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: var(--switch-inner);
width: var(--switch-inner);
left: 3px;
bottom: 3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(var(--switch-slide));
-ms-transform: translateX(var(--switch-slide));
transform: translateX(var(--switch-slide));
}
/* Rounded sliders */
.slider.round {
border-radius: var(--switch-inner);
}
.slider.round:before {
border-radius: 50%;
}
.checkgroup, select{
margin: 4px 8px;
}
select{
font-family: monospace;
font-weight: bold;
border: 2px solid black;
}
/* END */
</style>
</head>
<body>
<div class="markdown-src">
<!-- HTML -->
<span class='h4'>Chord2020 Editor</span>
<a href="https://gist.github.com/derekmc/158c0c4474a099bec653a10afc04155f"
title="Open Documentation" style="color:blue" target="_blank">Help</a>
<div class="controls" style='display: inline-block; float: right;'>
<span class='checkgroup' title='"Ctrl + Space"'>
<label class="switch">
<input id="show_codes" type="checkbox" checked onchange="WriteText('');
document.getElementById('textout').focus()">
<span class="slider round"></span>
</label>
<label for="show_codes">Show Codes</label>
</span>
<span class='checkgroup' title='"Ctrl + Enter"'>
<label class="switch">
<input id="enable_chords" type="checkbox" checked
onchange="document.getElementById('textout').focus()">
<span class="slider round"></span>
</label>
<label for="enable_chords">Chording</label>
</span>
<select id='layout_select' onchange="UpdateLayout()" title='"Ctrl + B"'>
<option value='qwerty'>qwerty</option>
<option value='dvorak'>dvorak</option>
<option value='colemak'>colemak</option>
<option value='colemakdh'>colemak-dh</option>
<option value='gallium'>gallium</option>
<option value='custom'>custom</option>
</select>
</div>
<textarea id='textout' readonly spellcheck="false"></textarea>
<button onclick="CutAction()">Cut</button>
<button onclick="CopyAction()">Copy</button>
<button onclick="PasteAction()">Paste</button>
&nbsp;
<span id='status' style="font-size: 14px;"></span>
<span id='typingstatus' style="float: right; font-size: 12px;"></span>
<!-- END -->
</div>
<div style="display: none;"></div>
<script>
let datakey = "__HTML_NOTES__:notedataid=sq6wJhV8AY"
let inframe = (window!=window.top)
window.Note = {}
window.Note.data =
/* JSON */
{"exclude":["re","pm","jan","uk","non","rss","faq","feb","sep","et","st","aug","apr","en","jul","ny","eur","usr","nj","em","rw","ne","du","nt","es","gb","pr","fr","aa","var","mt","usd","mg","les","ch","sd","rs","avg","src","pda","dsl","sql","ss","ap","nm","mn","nd","op","acc","tn","ce","der","tm","sp","nh","mysql","pdt","db","ia","pt","ds","und","lg","nw","ff","vat","ky","dont","br","lo","ml","res","cs","que","sf","ut","si","mc","qty","lp","ag","vi","edt","pmid","cr","pg","ee","ing","ks","ftp","sw","hd","er","gcc","asp","nv","su","exp","nr","mp","def","nl","tr","bb","nz","te","av","nsw","pci","cst","twiki","ec","rm","pdas","cf","vt","urw","nec","gm","ri","rt","cp","dd","pl","crm","rf","ak","td","sb","sm","wv","ns","bs","hrs","img","rpm","ll","cl","ieee","ae","hs","yr","ic","mx","gr","xhtml","ext","ts","ge","pe","tt","dv","val","fy","rr","ef","jp","bt","rfc","sl","ins","hb","tc","oem","zdnet","oclc","plc","msg","cb","fc","fw","gs","aaa","bp","std","oo","fs","nn","kde","vb","dl","ls","phpbb","je","cms","sg","nav","ist","lc","sys","icq","scsi","cu","pty","ws","np","tft","jvc","dt","gc","ci","yn","vii","cfr","pmc","nb","rx","ddr","this","dec","ii","ca","oct","nov","jun","mon","fri","cnet","ltd","hp","ip","tue","thu","tx","iii","gmt","fl","il","az","th","pp","au","nc","va","rd","sc","se","ga","sa","wi","ct","eq","jd","oe","wy","pd","uc","ld","mf","mw","nu","dp","ht","za","ve","rh","fg","bk","dx","sk","yu","oc","wp","jm","cn","rn","cg","ix","tb","bm","rj","ka","rp","hc","fp","jj","rl","gl","dh","df","pf","uw","bd","hh","fd","wm","pk","wb","sn","dk","fu","wn","sv","zu","dg","vg","ul","js","pn","wt","mv","rb","mh","ww","bw","lf","wr","rg","bl","wx","bg","kw","lm","hl","zus","cet","ppc","ons","tgp","ati","ict","asn","qld","llp","sbjct","apnic"],"wordIndex":1350,"text":"Buck did not read the newspapers, or he would have known that trouble was brewing, not alone for himself, but for every tid he have haven't e-water dog, strong of muscle and with warm, long hair, from Puget Sound to San Diego. Because men, groping in the Arctic darkness, had found a yellow metal, and because steamship and transportation companies were booming the find, thousands of men were rushing into the Northland. These men wanted dogs, and the dogs they wanted were heavy dogs, with strong muscles by which to toil, and furry coats to protect them from the frost.\r\n\r\nBuck lived at a big house in the sun-kissed Santa Clara Valley. Judge Miller’s place, it was called. It stood back from the road, half hidden among the trees, through which glimpses could be caught of the wide cool veranda that ran around its four sides. The house was approached by gravelled driveways which wound about through wide-spreading lawns and under the interlacing boughs of tall poplars. At the rear things were on even a more spacious scale than at the front. There were great stables, where a dozen grooms and boys held forth, rows of vine-clad servants’ cottages, an endless and orderly array of outhouses, long grape arbors, green pastures, orchards, and berry patches. Then there was the pumping plant for the artesian well, and the big cement tank where Judge Miller’s boys took their morning plunge and kept cool in the hot afternoon.\r\n\r\nAnd over this great demesne Buck ruled. Here he was born, and here he had lived the four years of his life. It was true, there were other dogs. There could not but be other dogs on so vast a place, but they did not count. They came and went, resided in the populous kennels, or lived obscurely in the recesses of the house after the fashion of Toots, the Japanese pug, or Ysabel, the Mexican hairless,—strange creatures that rarely put nose out of doors or set foot to ground. On the other hand, there were the fox terriers, a score of them at least, who yelped fearful promises at Toots and Ysabel looking out of the windows at them and protected by a legion of housemaids armed with brooms and mops.\r\n\r\nBut Buck was neither house-dog nor kennel-dog. The whole realm was his. He plunged into the swimming tank or went hunting with the Judge’s sons; he escorted Mollie and Alice, the Judge’s daughters, on long twilight or early morning rambles; on wintry nights he lay at the Judge’s feet before the roaring library fire; he carried the Judge’s grandsons on his back, or rolled them in the grass, and guarded their footsteps through wild adventures down to the fountain in the stable yard, and even beyond, where the paddocks were, and the berry patches. Among the terriers he stalked imperiously, and Toots and Ysabel he utterly ignored, for he was king,—king over all creeping, crawling, flying things of Judge Miller’s place, humans included.\r\n\r\nHis father, Elmo, a huge St. Bernard, had been the Judge’s inseparable companion, and Buck bid fair to follow in the way of his father. He was not so large,—he weighed only one hundred and forty pounds,—for his mother, Shep, had been a Scotch shepherd dog. Nevertheless, one hundred and forty pounds, to which was added the dignity that comes of good living and universal respect, enabled him to carry himself in right royal fashion. During the four years since his puppyhood he had lived the life of a sated aristocrat; he had a fine pride in himself, was even a trifle egotistical, as country gentlemen sometimes become because of their insular situation. But he had saved himself by not becoming a mere pampered house-dog. Hunting and kindred outdoor delights had kept down the fat and hardened his muscles; and to him, as to the cold-tubbing races, the love of water had been a tonic and a health preserver.\r\n\r\nAnd this was the manner of dog Buck was in the fall of 1897, when the Klondike strike dragged men from all the world into the frozen North. But Buck did not read the newspapers, and he did not know that Manuel, one of the gardener’s helpers, was an undesirable acquaintance. Manuel had one besetting sin. He loved to play Chinese lottery. Also, in his gambling, he had one besetting weakness—faith in a system; and this made his damnation certain. For to play a system requires money, while the wages of a gardener’s helper do not lap over the needs of a wife and numerous progeny.\r\n\r\nThe Judge was at a meeting of the Raisin Growers’ Association, and the boys were busy organizing an athletic club, on the memorable night of Manuel’s treachery. No one saw him and Buck go off through the orchard on what Buck imagined was merely a stroll. And with the exception of a solitary man, no one saw them arrive at the little flag station known as College Park. This man talked with Manuel, and money chinked between them.\r\n\r\n“You might wrap up the goods before you deliver ’m,” the stranger said gruffly, and Manuel doubled a piece of stout rope around Buck’s neck under the collar.\r\n\r\n“Twist it, an’ you’ll choke ’m plentee,” said Manuel, and the stranger grunted a ready affirmative.\r\n\r\nBuck had accepted the rope with quiet dignity. To be sure, it was an unwonted performance: but he had learned to trust in men he knew, and to give them credit for a wisdom that outreached his own. But when the ends of the rope were placed in the stranger’s hands, he growled menacingly. He had merely intimated his displeasure, in his pride believing that to intimate was to command. But to his surprise the rope tightened around his neck, shutting off his breath. In quick rage he sprang at the man, who met him halfway, grappled him close by the throat, and with a deft twist threw him over on his back. Then the rope tightened mercilessly, while Buck struggled in a fury, his tongue lolling out of his mouth and his great chest panting futilely. Never in all his life had he been so vilely treated, and never in all his life had he been so angry. But his strength ebbed, his eyes glazed, and he knew nothing when the train was flagged and the two men threw him into the baggage car.\r\n\r\nThe next he knew, he was dimly aware that his tongue was hurting and that he was being jolted along in some kind of a conveyance. The hoarse shriek of a locomotive whistling a crossing told him where he was. He had travelled too often with the Judge not to know the sensation of riding in a baggage car. He opened his eyes, and into them came the unbridled anger of a kidnapped king. The man sprang for his throat, but Buck was too quick for him. His jaws closed on the hand, nor did they relax till his senses were choked out of him once more.\r\n\r\n“Yep, has fits,” the man said, hiding his mangled hand from the baggageman, who had been attracted by the sounds of struggle. “I’m takin’ ’m up for the boss to ’Frisco. A crack dog-doctor there thinks that he can cure ’m.”\r\n\r\nConcerning that night’s ride, the man spoke most eloquently for himself, in a little shed back of a saloon on the San Francisco water front.\r\n\r\n“All I get is fifty for it,” he grumbled; “an’ I wouldn’t do it over for a thousand, cold cash.”\r\n\r\nHis hand was wrapped in a bloody handkerchief, and the right trouser leg was ripped from knee to ankle.\r\n\r\n“How much did the other mug get?” the saloon-keeper demanded.\r\n\r\n“A hundred,” was the reply. “Wouldn’t take a sou less, so help me.”\r\n\r\n“That makes a hundred and fifty,” the saloon-keeper calculated; “and he’s worth it, or I’m a squarehead.”\r\n\r\nThe kidnapper undid the bloody wrappings and looked at his lacerated hand. “If I don’t get the hydrophoby—”\r\n\r\n“It’ll be because you was born to hang,” laughed the saloon-keeper. “Here, lend me a hand before you pull your freight,” he added.\r\n\r\nDazed, suffering intolerable pain from throat and tongue, with the life half throttled out of him, Buck attempted to face his tormentors. But he was thrown down and choked repeatedly, till they succeeded in filing the heavy brass collar from off his neck. Then the rope was removed, and he was flung into a cagelike crate.\r\n\r\nThere he lay for the remainder of the weary night, nursing his wrath and wounded pride. He could not understand what it all meant. What did they want with him, these strange men? Why were they keeping him pent up in this narrow crate? He did not know why, but he felt oppressed by the vague sense of impending calamity. Several times during the night he sprang to his feet when the shed door rattled open, expecting to see the Judge, or the boys at least. But each time it was the bulging face of the saloon-keeper that peered in at him by the sickly light of a tallow candle. And each time the joyful bark that trembled in Buck’s throat was twisted into a savage growl.\r\n\r\nBut the saloon-keeper let him alone, and in the morning four men entered and picked up the crate. More tormentors, Buck decided, for they were evil-looking creatures, ragged and unkempt; and he stormed and raged at them through the bars. They only laughed and poked sticks at him, which he promptly assailed with his teeth till he realized that that was what they wanted. Whereupon he lay down sullenly and allowed the crate to be lifted into a wagon. Then he, and the crate in which he was imprisoned, began a passage through many hands. Clerks in the express office took charge of him; he was carted about in another wagon; a truck carried him, with an assortment of boxes and parcels, upon a ferry steamer; he was trucked off the steamer into a great railway depot, and finally he was deposited in an express car.\r\n\r\nFor two days and nights this express car was dragged along at the tail of shrieking locomotives; and for two days and nights Buck neither ate nor drank. In his anger he had met the first advances of the express messengers with growls, and they had retaliated by teasing him. When he flung himself against the bars, quivering and frothing, they laughed at him and taunted him. They growled and barked like detestable dogs, mewed, and flapped their arms and crowed. It was all very silly, he knew; but therefore the more outrage to his dignity, and his anger waxed and waxed. He did not mind the hunger so much, but the lack of water caused him severe suffering and fanned his wrath to fever-pitch. For that matter, high-strung and finely sensitive, the ill treatment had flung him into a fever, which was fed by the inflammation of his parched and swollen throat and tongue.\r\n\r\nHe was glad for one thing: the rope was off his neck. That had given them an unfair advantage; but now that it was off, he would show them. They would never get another rope around his neck. Upon that he was resolved. For two days and nights he neither ate nor drank, and during those two days and nights of torment, he accumulated a fund of wrath that boded ill for whoever first fell foul of him. His eyes turned blood-shot, and he was metamorphosed into a raging fiend. So changed was he that the Judge himself would not have recognized him; and the express messengers breathed with relief when they bundled him off the train at Seattle.\r\n\r\nFour men gingerly carried the crate from the wagon into a small, high-walled back yard. A stout man, with a red sweater that sagged generously at the neck, came out and signed the book for the driver. That was the man, Buck divined, the next tormentor, and he hurled himself savagely against the bars. The man smiled grimly, and brought a hatchet and a club.\r\n\r\n“You ain’t going to take him out now?” the driver asked.\r\n\r\n“Sure,” the man replied, driving the hatchet into the crate for a pry.\r\n\r\nThere was an instantaneous scattering of the four men who had carried it in, and from safe perches on top the wall they prepared to watch the performance.\r\n\r\nBuck rushed at the splintering wood, sinking his teeth into it, surging and wrestling with it. Wherever the hatchet fell on the outside, he was there on the inside, snarling and growling, as furiously anxious to get out as the man in the red sweater was calmly intent on getting him out.\r\n\r\n“Now, you red-eyed devil,” he said, when he had made an opening sufficient for the passage of Buck’s body. At the same time he dropped the hatchet and shifted the club to his right hand.\r\n\r\nAnd Buck was truly a red-eyed devil, as he drew himself together for the spring, hair bristling, mouth foaming, a mad glitter in his blood-shot eyes. Straight at the man he launched his one hundred and forty pounds of fury, surcharged with the pent passion of two days and nights. In mid air, just as his jaws were about to close on the man, he received a shock that checked his body and brought his teeth together with an agonizing clip. He whirled over, fetching the ground on his back and side. He had never been struck by a club in his life, and did not understand. With a snarl that was part bark and more scream he was again on his feet and launched into the air. And again the shock came and he was brought crushingly to the ground. This time he was aware that it was the club, but his madness knew no caution. A dozen times he charged, and as often the club broke the charge and smashed him down.\r\n\r\nAfter a particularly fierce blow, he crawled to his feet, too dazed to rush. He staggered limply about, the blood flowing from nose and mouth and ears, his beautiful coat sprayed and flecked with bloody slaver. Then the man advanced and deliberately dealt him a frightful blow on the nose. All the pain he had endured was as nothing compared with the exquisite agony of this. With a roar that was almost lionlike in its ferocity, he again hurled himself at the man. But the man, shifting the club from right to left, coolly caught him by the under jaw, at the same time wrenching downward and backward. Buck described a complete circle in the air, and half of another, then crashed to the ground on his head and chest.\r\n\r\nFor the last time he rushed. The man struck the shrewd blow he had purposely withheld for so long, and Buck crumpled up and went down, knocked utterly senseless.\r\n\r\n“He’s no slouch at dog-breakin’, that’s wot I say,” one of the men on the wall cried enthusiastically.\r\n\r\n“Druther break cayuses any day, and twice on Sundays,” was the reply of the driver, as he climbed on the wagon and started the horses.\r\n\r\nBuck’s senses came back to him, but not his strength. He lay where he had fallen, and from there he watched the man in the red sweater.\r\n\r\n“‘Answers to the name of Buck,’” the man soliloquized, quoting from the saloon-keeper’s letter which had announced the consignment of the crate and contents. “Well, Buck, my boy,” he went on in a genial voice, “we’ve had our little ruction, and the best thing we can do is to let it go at that. You’ve learned your place, and I know mine. Be a good dog and all ’ll go well and the goose hang high. Be a bad dog, and I’ll whale the stuffin’ outa you. Understand?”\r\n\r\nAs he spoke he fearlessly patted the head he had so mercilessly pounded, and though Buck’s hair involuntarily bristled at touch of the hand, he endured it without protest. When the man brought him water he drank eagerly, and later bolted a generous meal of raw meat, chunk by chunk, from the man’s hand.\r\n\r\nHe was beaten (he knew that); but he was not broken. He saw, once for all, that he stood no chance against a man with a club. He had learned the lesson, and in all his after life he never forgot it. That club was a revelation. It was his introduction to the reign of primitive law, and he met the introduction halfway. The facts of life took on a fiercer aspect; and while he faced that aspect uncowed, he faced it with all the latent cunning of his nature aroused. As the days went by, other dogs came, in crates and at the ends of ropes, some docilely, and some raging and roaring as he had come; and, one and all, he watched them pass under the dominion of the man in the red sweater. Again and again, as he looked at each brutal performance, the lesson was driven home to Buck: a man with a club was a lawgiver, a master to be obeyed, though not necessarily conciliated. Of this last Buck was never guilty, though he did see beaten dogs that fawned upon the man, and wagged their tails, and licked his hand. Also he saw one dog, that would neither conciliate nor obey, finally killed in the struggle for mastery.\r\n\r\nNow and again men came, strangers, who talked excitedly, wheedlingly, and in all kinds of fashions to the man in the red sweater. And at such times that money passed between them the strangers took one or more of the dogs away with them. Buck wondered where they went, for they never came back; but the fear of the future was strong upon him, and he was glad each time when he was not selected.\r\n\r\nYet his time came, in the end, in the form of a little weazened man who spat broken English and many strange and uncouth exclamations which Buck could not understand.\r\n\r\n“Sacredam!” he cried, when his eyes lit upon Buck. “Dat one dam bully dog! Eh? How moch?”\r\n\r\n“Three hundred, and a present at that,” was the prompt reply of the man in the red sweater. “And seem’ it’s government money, you ain’t got no kick coming, eh, Perrault?”\r\n\r\nPerrault grinned. Considering that the price of dogs had been boomed skyward by the unwonted demand, it was not an unfair sum for so fine an animal. The Canadian Government would be no loser, nor would its despatches travel the slower. Perrault knew dogs, and when he looked at Buck he knew that he was one in a thousand—“One in ten t’ousand,” he commented mentally.\r\n\r\nBuck saw money pass between them, and was not surprised when Curly, a good-natured Newfoundland, and he were led away by the little weazened man. That was the last he saw of the man in the red sweater, and as Curly and he looked at receding Seattle from the deck of the Narwhal, it was the last he saw of the warm Southland. Curly and he were taken below by Perrault and turned over to a black-faced giant called François. Perrault was a French-Canadian, and swarthy; but François was a French-Canadian half-breed, and twice as swarthy. They were a new kind of men to Buck (of which he was destined to see many more), and while he developed no affection for them, he none the less grew honestly to respect them. He speedily learned that Perrault and François were fair men, calm and impartial in administering justice, and too wise in the way of dogs to be fooled by dogs.\r\n\r\nIn the ’tween-decks of the Narwhal, Buck and Curly joined two other dogs. One of them was a big, snow-white fellow from Spitzbergen who had been brought away by a whaling captain, and who had later accompanied a Geological Survey into the Barrens. He was friendly, in a treacherous sort of way, smiling into one’s face the while he meditated some underhand trick, as, for instance, when he stole from Buck’s food at the first meal. As Buck sprang to punish him, the lash of François’s whip sang through the air, reaching the culprit first; and nothing remained to Buck but to recover the bone. That was fair of François, he decided, and the half-breed began his rise in Buck’s estimation.\r\n\r\nThe other dog made no advances, nor received any; also, he did not attempt to steal from the newcomers. He was a gloomy, morose fellow, and he showed Curly plainly that all he desired was to be left alone, and further, that there would be trouble if he were not left alone. “Dave” he was called, and he ate and slept, or yawned between times, and took interest in nothing, not even when the Narwhal crossed Queen Charlotte Sound and rolled and pitched and bucked like a thing possessed. When Buck and Curly grew excited, half wild with fear, he raised his head as though annoyed, favored them with an incurious glance, yawned, and went to sleep again.\r\n\r\nDay and night the ship throbbed to the tireless pulse of the propeller, and though one day was very like another, it was apparent to Buck that the weather was steadily growing colder. At last, one morning, the propeller was quiet, and the Narwhal was pervaded with an atmosphere of excitement. He felt it, as did the other dogs, and knew that a change was at hand. François leashed them and brought them on deck. At the first step upon the cold surface, Buck’s feet sank into a white mushy something very like mud. He sprang back with a snort. More of this white stuff was falling through the air. He shook himself, but more of it fell upon him. He sniffed it curiously, then licked some up on his tongue. It bit like fire, and the next instant was gone. This puzzled him. He tried it again, with the same result. The onlookers laughed uproariously, and he felt ashamed, he knew not why, for it was his dffirst snow.'","cursor":20578,"dictionary":"a from look say time about get make see to all give man she two also go many so up and have me some use as he more take very at her my tell want be here new than we because him no that well but his not the what by how now their when can I of them which come if on then who could in one there why day into onto these will do it or they with even its other thing would find just our think year first know out this you for like people those your is was are had word were said an each way write long has did number sound most over water call may down side been any work part place made live where after back little only round came show every good under name through form sentence great help low line differ turn cause much mean before move right boy old too same does set three air play small end put home read hand port large spell add land must big high such follow act ask men change went light kind off need house picture try us again animal point mother world near build self earth father head stand own page should country found answer school grow study still learn plant cover food sun four between state keep eye never last let thought city tree cross farm hard start might story saw far sea draw left late run while press close night real life few north open seem together next white children begin got walk example ease paper group always music both mark often letter until mile river car feet care second book carry took science eat room friend began idea fish mountain stop once base hear horse cut sure watch color face wood main enough plain girl usual young ready above ever red list though feel talk bird soon body dog family direct pose leave song measure door product black short numeral class wind question happen complete ship area half rock order fire south problem piece told knew pass since top whole king space heard best hour better true during hundred five remember step early hold west ground interest reach fast verb sing listen six table travel less morning ten simple several vowel toward war lay against pattern slow center love person money serve appear road map rain rule govern pull cold notice voice unit power town fine certain fly fall lead cry dark machine note wait plan figure star box noun field rest correct able pound done beauty drive stood contain front teach week final gave green oh quick develop ocean warm free minute strong special mind behind clear tail produce fact street inch multiply nothing course stay wheel full force blue object decide surface deep moon island foot system busy test record boat common gold possible plane stead dry wonder laugh thousand ago ran check game shape equate hot miss brought heat snow tire bring yes distant fill east paint language among grand ball yet wave drop heart am present heavy dance engine position arm wide sail material size vary settle speak weight general ice matter circle pair include divide syllable felt perhaps pick sudden count square reason length represent art subject region energy hunt probable bed brother egg ride cell believe fraction forest sit race window store summer train sleep prove lone leg exercise wall catch mount wish sky board joy winter sat written wild instrument kept glass grass cow job edge sign visit past soft fun bright gas weather month million bear finish happy hope flower clothe strange gone jump baby eight village meet root buy raise solve metal whether push seven paragraph third shall held hair describe cook floor either result burn hill safe cat century consider type law bit coast copy phrase silent tall sand soil roll temperature finger industry value fight lie beat excite natural view sense ear else quite broke case middle kill son lake moment scale loud spring observe child straight consonant nation dictionary milk speed method organ pay age section dress cloud surprise quiet stone tiny climb cool design poor lot experiment bottom key iron single stick flat twenty skin smile crease hole trade melody trip office receive row mouth exact symbol die least trouble shout except wrote seed tone join suggest clean break lady yard rise bad blow oil blood touch grew cent mix team wire cost lost brown wear garden equal sent choose fell fit flow fair bank collect save control decimal gentle woman captain practice separate difficult doctor please protect noon whose locate ring character insect caught period indicate radio spoke atom human history effect electric expect crop modern element hit student corner party supply bone rail imagine provide agree thus capital chair danger fruit rich thick soldier process operate guess necessary sharp wing create neighbor wash bat rather crowd corn compare poem string bell depend meat rub tube famous dollar stream fear sight thin triangle planet hurry chief colony clock mine tie enter major fresh search send yellow gun allow print dead spot desert suit current lift rose continue block chart hat sell success company subtract event particular deal swim term opposite wife shoe shoulder spread arrange camp invent cotton born determine quart nine truck noise level chance gather shop stretch throw shine property column molecule select wrong gray repeat require broad prepare salt nose plural anger claim continent oxygen sugar death pretty skill women season solution magnet silver thank branch match suffix especially fig afraid huge sister steel discuss forward similar guide experience score apple bought led pitch coat mass card band rope slip win dream evening condition feed tool total basic smell valley nor double seat arrive master track parent shore division sheet substance favor connect post spend chord fat glad original share station dad bread charge proper bar offer segment slave duck instant market degree populate chick dear enemy reply drink occur support speech nature range steam motion path liquid log meant quotient teeth shell neck information site news contact business web pm online services click service price date email health re used products data policy available copyright message software jan video info rights public books links review years privacy items user de research university january mail reviews program games days management united hotel item international ebay comments development report member details terms hotels local using results education national posted internet address community within states phone dvd shipping reserved forum based code prices website index being file link today technology project pages uk version sports related security county american photo members network computer systems following download without per access resources posts media pictures personal including directory location text rating rate government usa return students shopping account times sites digital profile previous events john hours image department title description non insurance another cd quality listing content private tools customer december movies college article york jobs source author different sale around canada teen stock training credit categories advanced sales english estate conditions windows photos gay thread category gallery register however june october november library really action series model features provided tv required accessories movie forums march la september questions july yahoo going medical dec server pc application cart staff articles san feedback looking issues april users topic comment financial things working standard tax below mobile blog payment equipment login programs offers legal recent park stores memory performance social august quote options rates america important ii activities club girls additional password latest something gift changes ca texas oct poker status browse issue building seller court february audio nov groups al easy given files release analysis request fax china making needs professional areas future committee cards problems london washington meeting rss become id california schools added reference companies listed learning delivery net popular film stories computers journal reports co welcome central images president council away includes australia discussion archive others entertainment agreement format society months safety friends faq edition cars messages marketing further updated association having provides david already studies specific feb living sep collection called arts display limited powered solutions means director daily beach due et electronics upon planning database says official mar average technical france pro microsoft conference environment records st district calendar costs style url statement update parts aug downloads miles resource applications document works bill apr federal hosting rules adult tickets centre requirements via cheap kids finance minutes gifts europe reading topics individual tips plus auto usually edit videos percent function getting global tech economic en player projects lyrics subscribe submit germany amount included risk thanks everything deals various words linux jul production commercial james advertising received treatment newsletter archives points knowledge magazine error camera jun currently construction toys registered golf domain methods chapter makes protection policies loan manager india taken sort listings models michael known cases engineering florida none wireless license paul friday annual published later sony shows corporate google church purchase customers active response hardware materials holiday chat designed along writing html countries loss brand discount higher effects created standards political increase advertise kingdom environmental stuff french storage japan doing loans shoes entry orders availability africa summary growth notes agency monday european activity although drug pics western income cash employment overall bay commission ad package contents seen players album regional supplies started administration institute views plans screen exchange types sponsored lines electronic across benefits needed apply someone ny anything printer effective organization asked eur sunday selection casino pdf tour menu volume anyone mortgage corporation inside mature role weeks addition usr executive running lower union jewelry according dc clothing mon com names robert homepage skills bush islands advice career military rental decision british teens pre facilities zip bid sellers cable opportunities taking values coming tuesday lesbian appropriate logo actually nice statistics client ok returns sample investment shown saturday christmas england culture flash ms george choice starting registration fri thursday courses consumer hi airport foreign artist outside furniture levels channel mode phones ideas wednesday structure fund contract button releases wed homes super male custom virginia almost located multiple asian distribution editor inn industrial potential cnet ltd los hp focus featured rooms female responsible inc communications associated thomas primary cancer numbers browser foundation eg friendly schedule documents communication purpose feature comes police everyone independent ip approach cameras physical operating maps medicine ratings chicago forms tue smith wanted developed unique survey prior telephone sport sources mexico population pa regular secure navigation operations","layoutName":"gallium","fingerKeys":["de4","fgvc","sw3","q1","ki8","jhnm","lo9","p-"],"dictLoaded":true,"correctLastKey":false,"customLayout":"\n qwert yuiop[]\\\n asdfg hjkl;'\n zxcvb nm,./\n "};
/* END */
(function(){
window.Note.save = saveData
window.Note.load = loadData
window.Note.autoSave = autoSave
function getNoteModule(){
if(!window['Note']) window.Note = {}
return window.Note
}
function loadData(){
let note = getNoteModule()
if(!inframe){
try{
let savedata = localStorage.getItem(datakey)
if(savedata && savedata.length){
note.data = JSON.parse(savedata)
}
window.addEventListener("message", onMessage)
} catch(e){
console.log("No local data")
console.log(e)
}
} else {
return note.data
}
}
let firstLogCall = true
let originalLogFunc = console.log
function log(x, ...rest){
if(firstLogCall)
document.body.appendChild(document.createElement("hr"))
else
document.body.appendChild(document.createElement("br"))
document.body.appendChild(
document.createTextNode(x + " " + rest.join(" ")))
firstLogCall = false
originalLogFunc(x, ...rest)
}
console.log = log
let SaveInterval = 500
window.addEventListener("load", ()=>{
convertMarkdown()
autoSave()
loadData() // dont wait for async
})
let saveIntervalRef = null
function autoSave(enable){
if(enable === undefined) enable = true
clearInterval(saveIntervalRef)
if(enable){
saveIntervalRef = window.setInterval(saveData, SaveInterval)
}
}
// this is preferred over a 'localstorage' polyfill for a frame,
// so the programmer doesn't assume this is the browsers localstorage
function saveData(){
let note = getNoteModule()
if(inframe){
let msg_obj = {
action: "saveData",
data: JSON.stringify(note['data'])
}
let message = JSON.stringify(msg_obj)
window.parent.postMessage(message)
} else {
localStorage.setItem(datakey, JSON.stringify(note['data']))
}
}
let md_subs = [
/(\n|^)\s*######\s([^\s#].*)\n*/g, "\n<h6 id=\"$2\">$2</h6>\n",
/(\n|^)\s*#####\s([^\s#].*)\n*/g, "\n<h5 id=\"$2\">$2</h5>\n",
/(\n|^)\s*####\s*([^\s#].*)\n*/g, "\n<h4 id=\"$2\">$2</h4>\n",
/(\n|^)\s*###\s*([^\s#].*)\n*/g, "\n<h3 id=\"$2\">$2</h3>\n",
/(\n|^)\s*##\s*([^\s#].*)\n*/g, "\n<h2 id=\"$2\">$2</h2>\n",
/(\n|^)\s*#\s*([^\s#].*)\n*/g, "\n<h1 id=\"$2\">$2</h1>\n",
/\n(\s*)[\*\-](.*)/g, '\n<ul><li>$2</li></ul>',
/\n+\n(?=[^#\n])/g, "\n\n<br><br>",
/\n+\n/g, "\n",
/__([^_\n]*)__/g, "<b>$1</b>",
/\*\*([^_\n]*)\*\*/g, "<b>$1</b>",
/_([^_\n]*)_/g, "<i>$1</i>",
/\*([^_\n]*)\*/g, "<i>$1</i>",
/\!\[([^\]\n]*)\]\(([^\)\n]*)\)/g, "<img src=\"$2\" alt=\"$1\"></img>",
/\[([^\]\n]*)\]\(([^\)\n]*)\)/g, "<a href=\"$2\" target=\"_blank\">$1</a>",
]
function onMessage(){
try{
let message = JSON.parse(e.data)
// console.log('received message', message)
if(message.hasOwnProperty("event")){
if(message.event == "keydown" && typeof keydown != "undefined"){
keydown(message)
}
if(message.event == "keyup" && typeof keyup != "undefined"){
keydown(message)
}
}
} catch(e){
console.warn("error processing message: " + e.data)
}
}
function convertMarkdown(){
let containers = document.getElementsByClassName("markdown-src")
for(let j=0; j<containers.length; ++j){
let container = containers[j]
let src = container.innerHTML
for(var i=0; i<md_subs.length-1; i += 2){
var search = md_subs[i]
var replace = md_subs[i+1]
src = src.replace(search, replace)
}
container.innerHTML = src
}
}
})()
//window.addEventListener('load', htmlNotesMainFunc)
//function htmlNotesMainFunc(){
/* JS */
window.addEventListener('keydown', keydownWindow)
async function CopyAction(){
localClipboard = data.text
if(navigator.clipboard){
await navigator.clipboard.writeText(localClipboard)
}
status('Copy')
}
async function PasteAction(){
if(navigator.clipboard){
localClipboard = await navigator.clipboard.readText()
}
WriteText(localClipboard)
status('Paste')
}
async function CutAction(){
CopyAction()
data.text = ''
WriteText('')
}
async function keydownWindow(e){
if(e.ctrlKey && e.key == 'Enter'){
let boxes = [id('enable_chords')]
for(let i=0; i<boxes.length; ++i){
let box = boxes[i]
box.checked = !box.checked
if(box.checked) break
}
WriteText('')
e.preventDefault()
e.stopPropagation()
}
if(e.ctrlKey && e.key == ' '){
// count through checkboxes in binary
id('show_codes').checked = !id('show_codes').checked
WriteText('')
e.preventDefault()
e.stopPropagation()
}
if(e.ctrlKey && e.key.toLowerCase() == 'x'){
await CutAction()
}
if(e.ctrlKey && e.key.toLowerCase() == 'c'){
await CopyAction()
}
if(e.ctrlKey && e.key.toLowerCase() == 'v'){
await PasteAction()
}
if(e.ctrlKey && e.key.toLowerCase() == 'b'){
NextLayout()
}
}
// start with this dictionary
let dict0 = qw`
a from look say time about get make see to
all give man she two also go many so up
and have me some use as he more take very
at her my tell want be here new than we
because him no that well but his not the what
by how now their when can I of them which
come if on then who could in one there why
day into onto these will do it or they with
even its other thing would find just our think year
first know out this you for like people those your
`
// additional dictionaries
// the first few dictionaries are sorted by frequency (deekayen, first20hours)
// the later dictionaries should be sorted by word length
// sort mode
let SORT_NONE = 0, SORT_LENGTH = 1
let DICT_CACHE_KEY_PREFIX = "__DICTIONARY_CACHE__"
let dictionaryURLs = [
[SORT_NONE, 'https://gist.githubusercontent.com/deekayen/4148741/raw/98d35708fa344717d8eee15d11987de6c8e26d7d/1-1000.txt'],
[SORT_NONE, 'https://raw.githubusercontent.com/first20hours/google-10000-english/refs/heads/master/google-10000-english-no-swears.txt'],
[SORT_NONE, 'https://raw.githubusercontent.com/first20hours/google-10000-english/refs/heads/master/20k.txt'],
[SORT_LENGTH, 'https://raw.githubusercontent.com/dolph/dictionary/refs/heads/master/popular.txt'],
[SORT_LENGTH, 'https://raw.githubusercontent.com/dolph/dictionary/refs/heads/master/ospd.txt'],
]
let shiftMap = `\`~1!2@3#4$5%6^7&8*9(0)-_=+
[{]}\\|;:'",<.>/?`.replaceAll(/\s+/g, '')
let singleLayouts = {
qwerty: `
qwert yuiop[]\\
asdfg hjkl;'
zxcvb nm,./
`,
dvorak: `
',.py fgcrl/=\\
aoeui dhtns-
;qjkx bmwvz`,
colemak: `
qwfpg jluy;[]\\
arstd hneio'
zxcvb km,./`,
colemakdh: `
qwfpb jluy;[]\\
arstg mneio'
xcdvz kh,./`,
gallium:`
bldcv jfou,[]\\
nrtsg yhaei-
xqmwz kp';.`
}
singleLayouts.custom = singleLayouts.qwerty
function UpdateLayout(){
data.layoutName = id('layout_select').value
}
function NextLayout(){
let names = Object.keys(singleLayouts)
let index = names.indexOf(data.layoutName)
if(index < 0) index = 0
else index = (index + 1) % names.length
while(names[index].length == 0)
index = (index + 1) % names.length
data.layoutName = names[index]
id('layout_select').value = data.layoutName
status('Layout: ' + data.layoutName)
}
function MapLayout(name, ch){
if(name == 'qwerty')
return ch
if(ch.length > 1)
return ch
let layout
if(data.layoutName =="custom")
layout = data.customLayout.replace(/\s/g, '')
else
layout = singleLayouts[name].replace(/\s/g, '')
let qwerty = singleLayouts.qwerty.replace(/\s/g, '')
if(!layout) return ch
let shiftCh = MapUnshift(ch)
if(shiftCh != ch){
let index = qwerty.indexOf(shiftCh)
return MapShift(layout[index])
}
let index = qwerty.indexOf(ch)
if(index < 0) return ch
return layout[index]
}
function MapUnshift(ch){
let index = shiftMap.indexOf(ch)
if(index < 0) return ch.toLowerCase()
if(index%2 == 0) return ch
return shiftMap[index - 1]
}
function MapShift(ch){
let index = shiftMap.indexOf(ch)
if(index < 0) return ch ? ch.toUpperCase() : ''
if(index % 2==1) return ch
return shiftMap[index+1]
}
//let data = {}
let data = Note.data
if(!data.text) data.text = ''
if(!data.cursor) data.cursor = data.text.length
if(!data.dictionary) data.dictionary = []
if(!data.layoutName) data.layoutName = 'qwerty'
if(!data.fingerKeys) data.fingerKeys = qw`de4 fgvc sw3 q1 ki8 jhnm lo9 p-`
if(!data.dictLoaded) data.dictLoaded = false
if(data.customLayout) singleLayouts.custom = data.customLayout
if(!data.hasOwnProperty('correctLastKey')) data.correctLastKey = false
let dict = dict0.slice()
let dictLookup = {}
let chordOrder = []
// keyAddress is how chords are made,
// an address is the possible finger positions for
// a given finger.
let keyAddress = {}
let fingerCount = 0
let fingerState = [0,0,0,0, 0,0,0,0]
let holdState = [0,0,0,0, 0,0,0,0] // to enable rolling through chords
let freezeState = [0,0,0,0, 0,0,0,0]
let shiftPress = false
let maxFingers = 0
let lastKey = null
let ctrlShortcuts = [' ', 'c', 'v', 'x', 'b', '\n', 'enter']
let nonAlphanums = /[^\p{N}\p{L}']+/u
let localClipboard = 'clipboard'
let textPreview = ''
let textCodes = ''
let base36 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
let cursorDownMove = 25
// the 2 and 3 key combos are created first,
// so that you can still use on limited keyboards.
let maxKeys = 6
//extra banned chords
let banList = qw``
// manual substitutions, removals and additions
let exclude_words = qw`
re pm jan uk non rss faq feb sep et st aug apr en jul ny eur
usr nj em rw ne du nt es gb pr fr aa var mt usd mg les ch sd
rs avg src pda dsl sql ss ap nm mn nd op acc tn ce der tm sp
nh mysql pdt db ia pt ds und lg nw ff ky dont br ml res cs sf
ut ag edt pmid cr pg ee ing ks sw hd er gcc asp nv su exp nr mp
def nl tr bb nz te av nsw pci cst twiki ec rm pdas cf vt urw nec
gm ri rt cp dd pl crm rf ak td sb sm wv ns bs hrs img rpm ll cl
ieee ae hs yr ic mx gr xhtml ext ts ge pe tt dv val fy rr ef jp
bt rfc sl ins hb tc oem zdnet oclc plc msg cb fc fw gs bp std oo
fs nn kde vb dl ls phpbb je cms sg nav ist lc sys icq scsi cu
pty ws np tft jvc dt gc ci yn vii cfr pmc nb rx ddr dec ii
ca oct nov jun mon fri cnet ltd hp ip tue thu tx iii gmt fl il
az th pp au nc va rd sc se ga sa wi ct eq jd oe wy pd uc ld mf
mw nu dp ht za ve rh fg bk dx sk yu oc wp jm cn rn cg ix tb bm
rj ka rp hc fp jj rl gl dh df pf uw bd hh fd wm pk wb sn dk fu
wn sv zu dg vg ul js pn wt mv rb mh ww bw lf wr rg bl wx bg kw
lm hl zus cet ppc ons tgp ati ict asn qld llp sbjct apnic cdna
qt dts pgp cz ob aaa mediawiki
wto ccd aud fcc eds gpl ac af gmc gtk loc oxley arxiv ru lu tg
pv vw dw jl mk wl sz bf sj ij xd wh xr bn wk jb jw xt fb ua kc
kt tw gw ao tl lr nf pw wg lv hf wd lj ke lx bv tk gf vm jh uu
kr jt dn hw xm fn ib gh gn yy gu ay jf kk bh kl pu px cy lh kd
xv ji hj sx rk ux vu sy ik hx ov cx fh ej dy qb jg xf hn vv wf
oa iu vf zz kj kv kx eo lw ju vo xy uf kh vn wj lq hy ih nk ud
lk kp vx gk hv aq gx fj gj gz tz xa ue kn py vl dq pq ub xu bx
jn dz zh kz cq xb qd iz gv ck ki bj ku vid bbw wal rrp ooo ste
tba boc tmp ala cio enb ata wma cir dsc wan sen dat ind qui slr
rel ign att ffl ppp ies xsl ctr vcd mst dmx str obj auf pkg sch
mfg blk bsd adv imc pcr mmc rms ont hsn sgh een rdf liu gta abt
svn twp atx bst nsf dcr kvm sts goa thr emi dst msa pcb crc chf
clr dba cdr agp exc fra rcw aps iis jsp dlp afp nfs lac edi pid
xii emc aix psc icc iec esa uml spi ppl hcl abn mfr afb tcl qos
dmc asc nsu wks rbi ics apa phe ses bsc bbb nws ngc kos psu ogg
tyr lsu uid umd gdb rpc thb idg dds cpc alr cme ecc crs ssi aac
cbd rfp sle scr pdb svc dcp pte ors cps fte stl gsa otc ssa itu
mpi mps nwt ths hgh oss wmv mgm csa nrc chr scm fno spd adc ubc
csu fsa xiv sgd ftd smb bse itk kms thn kpx spp mfc nmr mso htm
dhs msu fsb cns omb cpl sbs swf ipb tsn bdd csc gln stp avr cts
wmd cfs ffi tcm prc afc srl thx xvi ttl bmp dss ssk dsm spc pcg
snp adm snr pps ntp opp lps pdp gpo cpp crn mmf rtf bmc csr dvb
cdp mev sdn mta esd gnd osu utp jpy ccm bmg jbl pcm dtd hpa pvt
atp tec doi dsp dll iss cmp ich sbc dei gsm buf pts sst teh acct
adsl cdrw cdma dmoz divx embl exif hklm incl msdn mpls ngos staa
uefa undp wolverhampton commentsblog verzeichnis viewpicture
christchurch oscommerce viewsonic ret xtc addr attr ostg xnxx
xxxx xvid xslt xiii yyyy devel endif fomit bizkit bibtex fulfil
xenical zoofilia gcse engl mgmt odbc wwii sublimedirectory
`
let include_words = qw`
I'm you're there's he's she's we're can't won't wouldn't
couldn't I'd I'll isn't hadn't hasn't haven't he'd he'll
weren't let's didn't doesn't don't he'd she'd they'll that's
they'd who's you've you're you'll we've I've that's we're
lawns populous kennels obscurely recesses groping booming
hallow dedicate consecrated consecrate perish detract nobly
artesian hairless housemaids yelped fearful puget interlacing
boughs brooms mops ysabel stables pastures orchards arbors
foostep footsteps resided stalking elmo imperiously northland
terriers gravelled glimpses plunged rambles wintry grandsons
paddocks insular tubbing steamship sated aristocrat kindred
klondike besetted gruffly conveyance grunted growled menacingly
besetting damnation outreached vilely futilely chinked jolted
outhouses poplars grappled unbridled wolfish vicarious
assailants combatants tumbled intentness yelping onlooking
trampled defless cunningly deathless sorely buckles bristling
draught wagged appeasingly appeasement inoffensive antagonist
confines consternation disconsolate glowed warmly unmolested
reassured arduous confidently wriggled squirmed placatingly
diabolically introspective leaped ignominiously forlorn harking
spasmodically forlorn especial drooping ascended ventured
gladdened eagerness trice bristled snarled whirled roused
forbiddingly goldseekers snowdrifts snowdrift forebears webbed
fringed fastidiousness adaptability southland retrogression
toiled blunderer farthest loathsome indigestible unsuspected
heralded acuteness leeward cadences howled wolflike pasiveness
unconcern malingerer accommodated decivilization quickened
primeval heredity stoutest nutriment stiffness passiveness
trouncing comradeship fiercely
peculiarity surged clamor wrestled perpendicular sheltering
divined unheeded goaded lashings coverings limped whimpered
roared pandemonium resounding rampant forgetfulness swiftly
skulking marauders mornful contemplation treacherously overthrowing
resolutely thrusting crackled brutes frothing unpursued
harnessing worthily prospered harnesses daunted frowning fierceness
dubiously moccasins tamed deliberateness masterful preeminently
precipitate rashness stiffened braced warily greviously shrill
skirted wailings ordained insubordination softened unresisting
rejoinder appealingly shirks descended blundered uncontent
unnumbered moaned sobbed craftily borealis snowfall unswerving
chuckling overthrow curving bubbling enveloped joyfully shirked
quarrelled bickered unaltered squabbling barbarous faithfully
slyly ploughed snowshoe strove countered clashed bubbled grievously
mournful quarrelling jangling overthrown prostrate harked gulped
swaggering gripped purposed recuperated journeyers freighted
antagonists inexorable climes quivered keenly practised brooded
ghostly shrewdly hurled abjectly whined exultantly flitting
overhanging shrieked solidly leaden writhed expectant churned
remnant floundered sleds lingered uneasily gleefully scruff
threateningly whereupon clubbing undervalued dreamily alertness
infrequent stoppages stringy knotty uttered peered clutching
springiness resiliency catlike mightily excelled monotonous oftener
triumphantly unapproachable perplexed convulsive grinned sheepishly
deluged worshipful mushers contend roundly aspired busters seeming
contend involuntarily halted tinkled mournfully retraced hurriedly
whips unfastened boomed skyward sagged trembled locomotives
tormentor wrappings throttled grumbled tormentors unkempt assailed
sullenly parched fanned surcharged enthusiastically fearlessly
patted pounded fiercer uncouth exclamations yawned underhand
culprit bucked incurious throbbed propeller pervaded leashed
onlookers excitedly uproariously meditated growls perches gingerly
consignment growling splintering fetching oppressed
`
function MakeDictSubs(){
let xcount = 0
for(let i=0, j=0; i < exclude_words.length && j < include_words.length; ++i, ++j){
let word1 = exclude_words[i].toLowerCase()
let word2 = include_words[j]
let index1 = dictLookup[word1]
let index2 = dictLookup[word2.toLowerCase()]
let skip1 = index1 === undefined
let skip2 = index2 !== undefined
if(skip1 && !skip2){
--j
}
if(!skip1 && skip2){
--i
}
if(!skip1 && !skip2){
delete dictLookup[word1]
dictLookup[word2.toLowerCase()] = index1
dict[index1] = word2
xcount = j + 1
}
}
let lastindex = chordOrder.length - 1
for(let j=xcount; j<exclude_words.length; ++j){
let newword = dict[++lastindex]
let oldword = exclude_words[j]
let index = dictLookup[oldword]
delete dictLookup[oldword]
dictLookup[newword] = index
dict[index] = newword
}
//console.info('him her his wrestled',
// (qw`him her his wrestled`).map(x=>chordOrder[dictLookup[x]]))
}
window.addEventListener("load", Main)
async function Main(){
window.addEventListener('keydown', keydown)
window.addEventListener('keyup', keyup)
await Init()
FingerStatus()
id('textout').focus()
//id('noscript').innerHTML = ''
}
function wordFilter(word){
if(word.length == 1){
word = word.toLowerCase()
return word=='i' || word=='a'
}
return true
}
function AddDictLookup(dict, start){
if(!start) start = 0
for(let i=start; i<dict.length; ++i){
dictLookup[dict[i].toLowerCase()] = i
}
}
// functions that access global variables (or singleton class variables)
// should begin with a capital letter.
// TODO fallback on cached dictionaries
async function LoadDictionaries(){
dict = dict0.slice()
dictLookup = {} // clear dict lookup
AddDictLookup(dict, 0)
let actions = []
let loaded_dicts = []
// try to load dictionaries from localStorage cache first
for(let i=0; i<dictionaryURLs.length; ++i){
try{
let url = dictionaryURLs[i][1]
let cache_key = DICT_CACHE_KEY_PREFIX + url
let dict_text = localStorage.getItem(cache_key)
if(!dict_text) continue
let d = dict_text.trim().split(nonAlphanums)
d = d.filter(wordFilter)
let sortlength = dictionaryURLs[i][0] == SORT_LENGTH
if(sortlength)
d = d.sort((a,b)=>a.length - b.length)
loaded_dicts[i] = d
}catch(e){
console.error(e)
}
}
// load dictionaries from remote source
for(let i=0; i<dictionaryURLs.length; ++i){
actions.push(fetch(dictionaryURLs[i][1]).then(res=>res.text()))
}
let textvals = await Promise.all(actions)
for(let i=0; i<textvals.length; ++i){
let text = textvals[i]
let sortlength = dictionaryURLs[i][0] == SORT_LENGTH
if(text){
try{
let d;
let n = dict.length
let url = dictionaryURLs[i][1]
let cache_key = DICT_CACHE_KEY_PREFIX + url
localStorage.setItem(cache_key, text)
d = text.trim().split(nonAlphanums)
d = d.filter(wordFilter)
if(sortlength)
d = d.sort((a,b)=>a.length - b.length)
loaded_dicts[i] = d
}catch(e){
console.error(e)
}
}
}
for(let i=0; i<loaded_dicts.length; ++i){
let sortlength = dictionaryURLs[i][0] == SORT_LENGTH
let n = dict.length
let d = loaded_dicts[i]
if(d && d.length > 0){
for(let j=0; j<d.length; ++j){
let word = d[j].toLowerCase()
if(include_words.indexOf(word) == -1 && !dictLookup.hasOwnProperty(word)){
dict.push(word)}
}
AddDictLookup(dict, n)
}
}
data.dictLoaded = true
if(data.hasOwnProperty('dictionary') && typeof(data.dictionary) == 'string'){
let saved = data.dictionary.split(' ')
if(saved.length > dict.length){
dict = saved }
}
}
function EnumerateChordOrder(){
console.info("Enumerating Chords")
let bans = banList
chordOrder = []
let threechords = 999 // count the number of 2 and 3 length chords
let fingerstates = []
let {fingerKeys} = data
let finger_base = fingerKeys.map(x=>x.length + 1)
// max dictionary length
let n0 = finger_base.reduce(((x,y)=>x*y),1)
- finger_base.reduce((x,y)=>x+y-1) - 1
n0 = Math.min(n0, dict.length)
let maxscore = 1 +
finger_base[0]*finger_base[1]*finger_base[2]*finger_base[3] +
finger_base[4]*finger_base[5]*finger_base[6]*finger_base[7]
let m0 = 10*n0 // prevent infinite loop
// create 2 and 3 key combinations in the first pass,
// 4 key in the second pass, etc.
for(let mink=2, maxk=3; maxk <= maxKeys; mink = ++maxk){
//console.info(`Enumerating chords ${mink} to ${maxk}`)
let n = n0 - chordOrder.length, m = m0
// goes over chords diagonally
// counting up on the left hand and down on the right
let diagscore = 2
let diagindex = -1
while(n>0 && m > 0){
if(++diagindex == diagscore+1){
if(++diagscore > maxscore) break
diagindex = 0
}
let b = diagindex
let a = diagscore - b
let state = fingerKeys.map(x=>0)
let f = Math.floor
for(let j=0; j<4; ++j){
let c = finger_base[j]
let d = finger_base[j + 4]
state[j] += a % c
state[j + 4] += b % d
a = f(a/c)
b = f(b/d)
}
// the
let excess = a > 0 || b > 0
let keycount = state.reduce((x,y)=>x + (y? 1 : 0), 0)
if(!excess && keycount >= mink && keycount <= maxk){
fingerstates.push(state)
let code = ChordCode(state)
chordOrder.push(code)
--n
}
else --m
}
if(maxk == 3) threechords = chordOrder.length
console.info(`${chordOrder.length} <= ${maxk}-key chords.`)
}
let fingermap = x => [x[3], x[2], x[0], x[1], x[5], x[4], x[6], x[7]]
let header = ' #n keys fingers code word\n================================================\n'
let chordmap = chordOrder.map((x, i)=> '' +
('' + (i + 1)).padStart(6,' ') + ' '
+ fingermap(fingerstates[i].map((x, j)=>
('_' + fingerKeys[j])[x])).join('')
+ ' ' + fingermap(fingerstates[i]).map(
(x,i)=>(i==4?' ':'') + x).join('')
+ ' ' + x + (x.length ==2? ' ' : '') + ' ' + dict[i])
let maptext = header + chordmap.join('\n')
//console.info('chord map\n' + maptext)
//let out = id('chordListOut')
//out.innerHTML = ''
//out.appendChild(document.createTextNode(maptext))
// only save threechord words to local dictionary.
data.dictionary = dict.slice(0, threechords).join(' ')
}
async function Init(){
let {fingerKeys} = data
for(let i=0; i<fingerKeys.length; ++i){
//id('fing' + (i+1)).value = fingerKeys[i]
}
//id('maxKeysInput').value = maxKeys
if(true || !data.dictLoaded)
await LoadDictionaries()
EnumerateChordOrder()
MakeDictSubs()
keyAddress = {}
for(let i=0; i<fingerKeys.length; ++i){
let s = fingerKeys[i]
for(let j=0; j<s.length; ++j){
keyAddress[s[j]] = [i, j+1]
}
}
if(!data.customLayout) data.customLayout = singleLayouts.qwerty
id('layout_select').value = data.layoutName
status('init')
}
function ChordCode(state){
let {fingerKeys} = data
assert(fingerKeys.length >= 8, 'need 8 fingerkey sets')
let result = ""
let r = fingerKeys[0].length + 1,
s = fingerKeys[2].length + 1,
t = fingerKeys[4].length + 1,
u = fingerKeys[6].length + 1;
let a = base36[state[0] + r*state[1]]
let b = base36[state[2] + s*state[3]]
let c = base36[state[4] + t*state[5]]
let d = base36[state[6] + u*state[7]]
if(b=='0' && d=='0')
return a + c
else
return a + b + c + d
}
function TranslateTextToCodes(text){
let alphanum = /[\p{N}\p{L}']/u
let result = ''
let word = ''
let extrach = 0 // additional letters in the original text vs translated text
let extraMax = 0
for(let i=0; i<text.length; ++i){
let ch = text[i]
if(ch == '\n'){
if(extrach > extraMax){
extrach = extraMax }
}
if(ch.match(alphanum)) word += ch
else{
if(word.length){
let translated = TranslateWordToCodes(word)
extrach += word.length - translated.length
result += translated
while(extrach > extraMax){
result += '_'; --extrach}
word = ''
}
result += ch
}
}
if(word.length){
result += TranslateWordToCodes(word)
}
return result
}
//translates a word to one or more codes
function TranslateWordToCodes(word){
if(word == 'i') return word // dont translate a lowercase i to a code.
let compoundWords = false
let getIndex = (w)=>{
let i = dictLookup[w.toLowerCase()]
return i < chordOrder.length? i : -1
}
let getChord = (i)=>chordOrder[i]
let i = getIndex(word)
if(i > -1) return getChord(i)
if(!compoundWords) return word
let result = word
let score = word.length + 2
// try split
for(let j=1; j<word.length - 1; ++j){
let a = word.substring(0, j)
let b = word.substring(j)
let k = getIndex(a)
let l = getIndex(b)
if(k > -1 && l < 0){
if(score > b.length + 4){
score = b.length + 4
result = getChord(k) + b
}
}
if(k < 0 && l > -1){
if(score > a.length + 4){
score = a.length + 4
result = a + getChord(l)
}
}
if(k > -1 && l > -1){
if(score >= 8){
score = 8
result = getChord(k) + "|" + getChord(l)
}
}
}
return result
}
function FingerStatus(){
let fs = fingerState
fs = '' + fs[3] + fs[2] + fs[0] + fs[1] + fs[5] + fs[4] + fs[6] + fs[7]
let code = ChordCode(fingerState.map((x,i)=>x>0? x : holdState[i]))
let freezecode = ChordCode(freezeState)
let i = chordOrder.indexOf(code)
let j = chordOrder.indexOf(freezecode)
let word = i >= 0 ? dict[i] : ''
if(word.length && shiftPress)
word = word[0].toUpperCase() + word.slice(1)
let freezeword = j >= 0? dict[j] : ''
if(freezeword.length && shiftPress)
freezeword = freezeword[0].toUpperCase() + freezeword.slice(1)
let lastword = GetLastWord()
s = ''
s += `&emsp;Active word: [${code=='00-00'? '' : code} ${word}]`
s +='&emsp;Fingers: ' + fs.substr(0,4) + ' ' + fs.substr(4)
s += `&emsp;Last word: [${LookupWordCode(lastword)} ${lastword}]`
id('typingstatus').innerHTML = s
let non_alphanum = /[^\p{N}\p{L}']/u
let count =
holdState.reduce(((x,y)=>x + (y>0? 1:0)), 0) +
fingerState.reduce(((x,y)=>x + (y>0? 1:0)), 0)
if(count > 1){
let lastspace = data.text.length == 0 || data.text[data.text.length - 1].match(non_alphanum)
textPreview = (lastspace? ' ' : '') + '[' + freezecode + ' ' + freezeword + ']'
} else {
textPreview = '|'
}
WriteText('')
}
function keydown(e){
let {key} = e
enter('textout')
let immediate = ["Backspace", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"]
let noevent = [" "]
if(noevent.indexOf(key) != -1){
e.preventDefault()
e.stopPropagation()
}
if(e.ctrlKey && ctrlShortcuts.indexOf(key.toLowerCase()) != -1){
return
}
if(immediate.indexOf(key) != -1 || !id('enable_chords').checked){
return Input(key)
}
let upper = key.length == 1 && key.match(/[A-Z]/)
if(upper) key = key.toUpperCase()
if(e.shiftKey){
shiftPress = true
status('^^^')
}
let k = key.toLowerCase()
lastKey = k
if(k in keyAddress){
e.preventDefault()
e.stopPropagation()
let [i, j] = keyAddress[k]
if(fingerState[i] == 0) ++fingerCount
fingerState[i] = j
}
let holdcount = holdState.reduce(
((x,y)=>x + (y>0? 1:0)), 0)
if(fingerCount + holdcount >= maxFingers){
freezeState = fingerState.map((x,i)=>
x>0? x : holdState[i])
maxFingers = fingerCount + holdcount
}
FingerStatus()
}
let statusTimeout = 8000
let clearStatus = null
function status(x){
clearTimeout(clearStatus)
let y = id('status')
y.innerHTML = ''
y.appendChild(document.createTextNode(x))
clearStatus = setTimeout(()=>status(''), statusTimeout)
}
function keyup(e){
let {key} = e
if(e.ctrlKey && ctrlShortcuts.indexOf(key.toLowerCase()) != -1){
return
}
if(!id('enable_chords').checked){
return
}
let upper = key.length == 1 && key.match(/[A-Z]/)
if(upper) key = key.toUpperCase()
if(e.shiftKey){
shiftPress = true
status('^^^')
}
if(key == 'Shift') return
let k = key.toLowerCase()
if(k in keyAddress){
let [i, j] = keyAddress[k]
fingerState[i] = 0
if(!data.correctLastKey || k != lastKey){
holdState[i] = j}
--fingerCount
if(fingerCount == 0){
if(maxFingers > 1){
Emit(freezeState, shiftPress)
freezeState = fingerState.slice(0)
shiftPress = false
status('')
}else{
Input(key)
shiftPress = false
status('')
}
holdState = fingerState.slice(0)
maxFingers = 0
}
} else {
let exclude = ["Backspace", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"]
if(fingerCount == 0 && maxFingers <= 1 && exclude.indexOf(key) == -1)
Input(key)
shiftPress = false
status('')
}
FingerStatus()
}
function WriteText(x){
if(data.cursor > data.text.length)
data.cursor = data.text.length
if(data.cursor < 0)
data.cursor = 0
let before = data.text.substr(0, data.cursor)
let after = data.text.substr(data.cursor)
data.text = before + x + after
data.cursor += x.length
before += x
let text = before + textPreview + after
let translate = TranslateTextToCodes
let preword = '' // before and after cursor words
let postword = ''
for(let i=before.length - 1; i>=0; --i){
if(before[i].match(nonAlphanums)) break
preword = before[i] + preword
before = before.substr(0, i)
}
while(after.length > 0){
if(after[0].match(nonAlphanums)) break
postword += after[0]
after = after.substr(1)
}
let text2 = translate(before) + preword + textPreview + postword + translate(after)
let out = id('textout')
out.innerHTML = ''
let showCodes = id('show_codes').checked
out.appendChild(document.createTextNode(showCodes? text2 : text))
}
function Emit(state, shift){
textPreview = ''
let a = ChordCode(state)
let i = chordOrder.indexOf(a)
if(i<0) return
let b = dict[i]
if(shift)
b = b[0].toUpperCase() + b.slice(1)
let alphanum = /[\p{N}\p{L}']/u
if(data.cursor > 0 && !data.text[data.cursor - 1].match(/[\s]/))
b = ' ' + b
WriteText(b + ' ')
}
function Input(key){
if(key.length == 1){
if(shiftPress) key = MapShift(key)
shiftPress = false
key = MapLayout(data.layoutName, key)
if(key != ' ' && key.match(nonAlphanums) &&
id('enable_chords').checked &&
data.cursor > 0 && data.text[data.cursor - 1] == ' '){
Input('Backspace')
}
WriteText(key)
}else{
if(key == 'Home'){
data.cursor = 0
WriteText('')
}
if(key == 'End'){
data.cursor = data.text.length
WriteText('')
}
if(key == 'ArrowUp'){
if(data.cursor > 0)
for(let i=0; i<cursorDownMove && data.cursor > 0; ++i)
--data.cursor
WriteText('')
}
if(key == 'ArrowDown'){
if(data.cursor < data.text.length)
for(let i=0; i<cursorDownMove && data.cursor < data.text.length; ++i)
++data.cursor
WriteText('')
}
if(key == 'ArrowLeft'){
if(data.cursor > 0) --data.cursor
WriteText('')
}
if(key == 'ArrowRight'){
if(data.cursor < data.text.length) ++data.cursor
WriteText('')
}
if(key == 'Backspace'){
let n = data.cursor
let before = data.text.substr(0, n)
let after = data.text.substr(n)
let remove = 1
let alphanum = /[\p{N}\p{L}']/u
if(n > 0 && id('enable_chords').checked && data.text[n - 1].match(alphanum))
while(n - remove > 0 && data.text[n - remove - 1].match(alphanum))
++remove
n = Math.max(0, n - remove)
data.cursor -= remove
data.text = before.substring(0, n) + after
WriteText('')
}
if(key=='Enter'){
WriteText('\n')
}
if(key=="Shift"){
shiftPress = true
status('^^^')
}
}
//console.log(key)
}
function assert(x, msg){
if(x) return
if(!msg) msg = "Assertion failed."
else msg = "Assertion failed: " + msg
console.error(msg)
throw new Error(msg)
}
function GetLastWord(){
let b = data.text.length
let alphanum = /[\p{N}\p{L}']/u
while(--b >= 0 && !data.text[b].match(alphanum));
let a = b
while(--a >= 0 && data.text[a].match(alphanum));
return data.text.substring(a+1, b+1)
}
function LookupWordCode(s){
//console.log(dictLookup)
let i = dictLookup[s.toLowerCase()]
if(isNaN(i) || i >=chordOrder.length) return ''
return chordOrder[i]
}
function qw(parts){
return parts.join('').trim().split(/\s+/)
}
function id(x){
return document.getElementById(x)
}
function enter(x){
id(x).focus()
}
/* END */
//}
</script>
</body>
<html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment