lib.js (27064B)
1 (function (root) { 2 'use strict'; 3 4 // ── Mad Libs templates ────────────────────────────────────────────────────── 5 // Every story is adapted from a public-domain children’s classic (see the 6 // `source` field — surfaced to parents in the page footer). Kid-friendly 7 // category labels only (never "noun/verb/adjective"). 8 // `story` uses {key} placeholders matching each blank's `key`. 9 10 const MADLIB_TEMPLATES = [ 11 { 12 id: 'peter-rabbit', 13 title: 'The Tale of Peter Rabbit', 14 source: { 15 title: 'The Tale of Peter Rabbit', 16 author: 'Beatrix Potter', 17 url: 'https://www.gutenberg.org/ebooks/14838', 18 }, 19 blanks: [ 20 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 21 { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, 22 { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, 23 { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, 24 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 25 ], 26 story: 'Once upon a time there were four little rabbits, and the naughtiest one was named {name}. ' + 27 'Mother said, "Do not go into Mr. McGregor’s garden!" But {name} squeezed under the gate ' + 28 'and ate some {food} until his tummy felt funny. Then Mr. McGregor came running, waving a rake! ' + 29 '{name} lost one {color} shoe among the cabbages and hid in a watering can with a little {animal}. ' + 30 'He sneezed "{silly}!" and ran all the way home to bed.', 31 }, 32 { 33 id: 'three-pigs', 34 title: 'The Three Little Pigs', 35 source: { 36 title: 'The Story of the Three Little Pigs (English Fairy Tales)', 37 author: 'Joseph Jacobs', 38 url: 'https://www.gutenberg.org/ebooks/7439', 39 }, 40 blanks: [ 41 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 42 { key: 'stuff', label: 'Tell me something you can build with!', voice: 'Tell me something you can build with!' }, 43 { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, 44 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 45 ], 46 story: 'There were three little pigs, and the cleverest one was named {name}. ' + 47 'The first built a house of straw, the second a house of sticks, but {name} built a strong house of {stuff}. ' + 48 'Along came the wolf and called, "Little pig, little pig, let me come in!" ' + 49 '"Not by the hair of my chinny chin chin!" said {name}. ' + 50 'So the wolf huffed, and puffed, and shouted "{silly}!" — but the house of {stuff} would not fall down. ' + 51 'The wolf ran away, and {name} had {food} for supper.', 52 }, 53 { 54 id: 'three-bears', 55 title: 'The Story of the Three Bears', 56 source: { 57 title: 'The Story of the Three Bears (English Fairy Tales)', 58 author: 'Joseph Jacobs', 59 url: 'https://www.gutenberg.org/ebooks/7439', 60 }, 61 blanks: [ 62 { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, 63 { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, 64 { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, 65 { key: 'action', label: 'Tell me something you can do!', voice: 'Tell me something you can do!' }, 66 ], 67 story: 'Three bears lived in a little house in the wood, and each one had a bowl of {food}. ' + 68 'While it cooled, a girl named {name} walked right in. ' + 69 'She tasted the great big bowl — too hot! The middle bowl — too cold! ' + 70 'The teeny tiny bowl was just right, so she ate it all up. ' + 71 'Then she sat in the teeny tiny chair until it went CRASH, and fell fast asleep upstairs. ' + 72 'When the bears came home the teeny tiny bear squeaked, "Somebody has been eating my {food}!" ' + 73 '{name} woke up, saw a {animal} peeking through the window, and began to {action} all the way home.', 74 }, 75 { 76 id: 'lion-mouse', 77 title: 'The Lion and the Mouse', 78 source: { 79 title: 'The Lion and the Mouse (The Fables of Æsop)', 80 author: 'Æsop, told anew by Joseph Jacobs', 81 url: 'https://www.gutenberg.org/ebooks/28', 82 }, 83 blanks: [ 84 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 85 { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, 86 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 87 ], 88 story: 'A lion was fast asleep in {place} when a little mouse named {name} ran right across his nose. ' + 89 'The lion woke with a roar and caught the mouse in his big paw. ' + 90 '"Please let me go," said {name}, "and one day I will help you!" ' + 91 'The lion laughed "{silly}!" and let the mouse go. ' + 92 'Not long after, hunters tied the lion up with ropes — and tiny {name} nibbled every rope through. ' + 93 'Even a little friend can be a big help.', 94 }, 95 { 96 id: 'hare-tortoise', 97 title: 'The Hare and the Tortoise', 98 source: { 99 title: 'The Hare and the Tortoise (The Fables of Æsop)', 100 author: 'Æsop, told anew by Joseph Jacobs', 101 url: 'https://www.gutenberg.org/ebooks/28', 102 }, 103 blanks: [ 104 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 105 { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, 106 { key: 'number', label: 'Tell me a number!', voice: 'Tell me a number!' }, 107 { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, 108 ], 109 story: 'A hare was bragging how fast he could run, so a slow tortoise named {name} said, ' + 110 '"I will race you all the way to {place}!" The hare laughed and zoomed ahead. ' + 111 'He was so far in front that he stopped for a nap and {number} bites of {food}. ' + 112 'Plod, plod, plod went {name}, never stopping once. ' + 113 'When the hare woke up and ran his fastest, {name} was already there. Slow and steady wins the race!', 114 }, 115 { 116 id: 'town-country-mouse', 117 title: 'The Town Mouse and the Country Mouse', 118 source: { 119 title: 'The Town Mouse and the Country Mouse (The Fables of Æsop)', 120 author: 'Æsop, told anew by Joseph Jacobs', 121 url: 'https://www.gutenberg.org/ebooks/28', 122 }, 123 blanks: [ 124 { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, 125 { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, 126 { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, 127 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 128 ], 129 story: 'A town mouse named {name} came to visit a country mouse, who gave him beans and crusts for dinner. ' + 130 '"Come to my house in town," said {name}, "and you shall have {food} instead!" ' + 131 'So off they went, and the table in town was piled high. ' + 132 'But just as they began to nibble, a big {animal} came in and they had to squeak "{silly}!" and hide in a hole. ' + 133 'The country mouse said, "I would rather have beans in peace than {food} in fright," and went home.', 134 }, 135 { 136 id: 'ugly-duckling', 137 title: 'The Ugly Duckling', 138 source: { 139 title: 'The Ugly Duckling (Andersen’s Fairy Tales)', 140 author: 'Hans Christian Andersen', 141 url: 'https://www.gutenberg.org/ebooks/1597', 142 }, 143 blanks: [ 144 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 145 { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, 146 { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, 147 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 148 ], 149 story: 'Down by the water, a mother duck’s eggs went crack, crack, crack. ' + 150 'Out came the ducklings — and the very last one was big and {color}, so everyone called him {name}. ' + 151 '"You look funny!" said the other birds, and poor {name} waddled away all by himself. ' + 152 'He was cold all winter, with only a little {food} to eat. ' + 153 'But in the spring he looked into the water and said "{silly}!" — ' + 154 'for he was not a duckling at all. He had grown into a beautiful swan.', 155 }, 156 { 157 id: 'emperors-clothes', 158 title: 'The Emperor’s New Clothes', 159 source: { 160 title: 'The Emperor’s New Clothes (Andersen’s Fairy Tales)', 161 author: 'Hans Christian Andersen', 162 url: 'https://www.gutenberg.org/ebooks/1597', 163 }, 164 blanks: [ 165 { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, 166 { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, 167 { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, 168 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 169 ], 170 story: 'There was an emperor who loved new clothes more than anything. ' + 171 'Two tricky tailors said they could sew him a {color} suit so magic that only clever people could see it. ' + 172 'They sewed with nothing at all! But nobody wanted to look silly, so everyone said, "How fine!" ' + 173 'The emperor marched down the street in his underwear, with a {animal} following behind. ' + 174 'Then a little kid named {name} shouted, "{silly}! He has no clothes on!" ' + 175 'And everybody laughed, because it was true.', 176 }, 177 { 178 id: 'elves-shoemaker', 179 title: 'The Elves and the Shoemaker', 180 source: { 181 title: 'The Elves and the Shoemaker (Grimms’ Fairy Tales)', 182 author: 'Jacob and Wilhelm Grimm', 183 url: 'https://www.gutenberg.org/ebooks/2591', 184 }, 185 blanks: [ 186 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 187 { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, 188 { key: 'number', label: 'Tell me a number!', voice: 'Tell me a number!' }, 189 { key: 'action', label: 'Tell me something you can do!', voice: 'Tell me something you can do!' }, 190 ], 191 story: 'A shoemaker named {name} had only enough leather left for one pair of shoes, ' + 192 'so {name} cut it out and went to bed. ' + 193 'In the morning — surprise! — the shoes were already finished, and they were {color} and perfect. ' + 194 'This happened again and again, until {name} peeked and saw {number} tiny elves ' + 195 'hop onto the table and {action} all night long. ' + 196 'So {name} sewed them tiny coats to say thank you, and the elves danced right out the door.', 197 }, 198 { 199 id: 'bremen-musicians', 200 title: 'The Bremen Town Musicians', 201 source: { 202 title: 'The Bremen Town Musicians (Grimms’ Fairy Tales)', 203 author: 'Jacob and Wilhelm Grimm', 204 url: 'https://www.gutenberg.org/ebooks/2591', 205 }, 206 blanks: [ 207 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 208 { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, 209 { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, 210 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 211 ], 212 story: 'An old donkey named {name} set off to be a musician in {place}. ' + 213 'Along the way a dog, a cat, a rooster, and even a {animal} came too. ' + 214 'When night fell they found a little house full of robbers, so they stood one on top of the other ' + 215 'and sang their song: hee-haw, woof, meow, cock-a-doodle-doo, "{silly}!" ' + 216 'The robbers ran away as fast as they could, and the four friends slept there happily ever after.', 217 }, 218 { 219 id: 'frog-prince', 220 title: 'The Frog Prince', 221 source: { 222 title: 'The Frog Prince (Grimms’ Fairy Tales)', 223 author: 'Jacob and Wilhelm Grimm', 224 url: 'https://www.gutenberg.org/ebooks/2591', 225 }, 226 blanks: [ 227 { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, 228 { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, 229 { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, 230 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 231 ], 232 story: 'A princess named {name} was playing with a {color} golden ball when — plop! — it fell into the well. ' + 233 '"I will fetch it," said a frog, "if you let me eat {food} from your little plate." ' + 234 '{name} said yes, and the frog dived down and brought the ball back up. ' + 235 'That night the frog knocked at the palace door: tap, tap, "{silly}!" ' + 236 'A promise is a promise, so {name} let him in — and in the morning the frog was a prince.', 237 }, 238 { 239 id: 'jack-beanstalk', 240 title: 'Jack and the Beanstalk', 241 source: { 242 title: 'Jack and the Beanstalk (English Fairy Tales)', 243 author: 'Joseph Jacobs', 244 url: 'https://www.gutenberg.org/ebooks/7439', 245 }, 246 blanks: [ 247 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 248 { key: 'number', label: 'Tell me a number!', voice: 'Tell me a number!' }, 249 { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, 250 { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, 251 ], 252 story: 'A boy named {name} traded the family cow for {number} magic beans. ' + 253 'His mother tossed them out the window — and by morning a beanstalk had grown up past the clouds! ' + 254 '{name} climbed and climbed, all the way to a giant’s castle in the sky. ' + 255 'The giant roared, "Fee-fi-fo-fum!" but he was busy eating {food}, ' + 256 'so {name} grabbed the hen that laid golden eggs and a {animal} that could sing. ' + 257 'Down the beanstalk he scrambled, chop-chop, and he and his mother were never hungry again.', 258 }, 259 { 260 id: 'red-riding-hood', 261 title: 'Little Red Riding Hood', 262 source: { 263 title: 'Little Red Riding Hood (English Fairy Tales)', 264 author: 'Flora Annie Steel', 265 url: 'https://www.gutenberg.org/ebooks/17034', 266 }, 267 blanks: [ 268 { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, 269 { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, 270 { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, 271 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 272 ], 273 story: 'A little girl named {name} had a {color} hooded cape, so everyone called her Little {color} Riding Hood. ' + 274 'One morning she carried a basket of {food} through the wood to her grandmother’s house. ' + 275 'A sly wolf hurried on ahead and hopped into Grandmother’s bed. ' + 276 '"What big eyes you have!" said {name}. "What big ears you have!" ' + 277 'Then the woodcutter came in shouting "{silly}!" and the wolf ran off into the trees. ' + 278 'Grandmother came out of the closet, and they all shared the {food}.', 279 }, 280 { 281 id: 'elephants-child', 282 title: 'The Elephant’s Child', 283 source: { 284 title: 'The Elephant’s Child (Just So Stories)', 285 author: 'Rudyard Kipling', 286 url: 'https://www.gutenberg.org/ebooks/2781', 287 }, 288 blanks: [ 289 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 290 { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, 291 { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, 292 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 293 ], 294 story: 'In the long ago, elephants had no trunks at all — only a little squashy nose. ' + 295 'But one baby elephant named {name} was full of questions, like ' + 296 '"What does the crocodile have for dinner?" ' + 297 'So {name} went all the way to {place} to find out, and the crocodile grabbed that little nose and PULLED. ' + 298 '{name} pulled back, and pulled, and hollered "{silly}!" — ' + 299 'and when the crocodile let go, the nose had stretched into a long, useful trunk. ' + 300 'Now {name} could pick {food} right off the tallest tree.', 301 }, 302 { 303 id: 'camel-hump', 304 title: 'How the Camel Got His Hump', 305 source: { 306 title: 'How the Camel Got His Hump (Just So Stories)', 307 author: 'Rudyard Kipling', 308 url: 'https://www.gutenberg.org/ebooks/2781', 309 }, 310 blanks: [ 311 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 312 { key: 'action', label: 'Tell me something you can do!', voice: 'Tell me something you can do!' }, 313 { key: 'number', label: 'Tell me a number!', voice: 'Tell me a number!' }, 314 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 315 ], 316 story: 'At the beginning of the world, a lazy camel named {name} lived in the middle of the desert. ' + 317 'The horse said, "Come and {action} with us!" The dog said it too, and the ox said it too. ' + 318 'But {name} only answered, "{silly}!" and would not work at all. ' + 319 'So the Djinn of the desert came along and gave {name} a great big humph — I mean, hump — ' + 320 'right on his back. Now {name} can work {number} whole days without stopping for dinner, ' + 321 'and camels have had humps ever since.', 322 }, 323 { 324 id: 'wizard-of-oz', 325 title: 'The Wonderful Wizard of Oz', 326 source: { 327 title: 'The Wonderful Wizard of Oz', 328 author: 'L. Frank Baum', 329 url: 'https://www.gutenberg.org/ebooks/55', 330 }, 331 blanks: [ 332 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 333 { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, 334 { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, 335 { key: 'action', label: 'Tell me something you can do!', voice: 'Tell me something you can do!' }, 336 ], 337 story: 'A cyclone picked up a little house with {name} and a small dog inside, and set it down in the land of Oz. ' + 338 '"We are not in Kansas any more!" said {name}. ' + 339 'To get home, {name} followed the road of yellow brick, ' + 340 'and along the way met a scarecrow, a tin woodman, a lion, and a friendly {animal}. ' + 341 'In the {color} City the great wizard turned out to be an ordinary man behind a curtain, ' + 342 'but he could still {action}, and the silver shoes carried {name} all the way home.', 343 }, 344 { 345 id: 'alice-rabbit-hole', 346 title: 'Alice Down the Rabbit Hole', 347 source: { 348 title: 'Alice’s Adventures in Wonderland', 349 author: 'Lewis Carroll', 350 url: 'https://www.gutenberg.org/ebooks/11', 351 }, 352 blanks: [ 353 { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, 354 { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, 355 { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, 356 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 357 ], 358 story: 'A girl named {name} saw a white rabbit run past, saying, "Oh dear! I shall be late!" ' + 359 'Down the rabbit hole she went, tumbling and tumbling. ' + 360 'At the bottom she found a little bottle marked DRINK ME and grew very small, ' + 361 'then a bit of {food} marked EAT ME and grew very TALL. ' + 362 'A grinning {animal} showed her the way to a tea party where everyone shouted "{silly}!" ' + 363 'and passed the cups around. What a curious dream {name} had that day.', 364 }, 365 { 366 id: 'velveteen-rabbit', 367 title: 'The Velveteen Rabbit', 368 source: { 369 title: 'The Velveteen Rabbit', 370 author: 'Margery Williams Bianco', 371 url: 'https://www.gutenberg.org/ebooks/11757', 372 }, 373 blanks: [ 374 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 375 { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, 376 { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, 377 { key: 'action', label: 'Tell me something you can do!', voice: 'Tell me something you can do!' }, 378 ], 379 story: 'On Christmas morning a boy named {name} found a {color} velveteen rabbit in his stocking. ' + 380 'The old rocking horse said, "When a child loves you for a long, long time, you become Real." ' + 381 '{name} took the rabbit everywhere — even to {place} — and hugged him until his fur was rubbed away. ' + 382 '"Am I Real yet?" asked the rabbit. ' + 383 'Then one spring day a fairy kissed him, and the rabbit found he could hop and {action} ' + 384 'with all the wild rabbits. He was Real at last.', 385 }, 386 { 387 id: 'tom-kitten', 388 title: 'The Tale of Tom Kitten', 389 source: { 390 title: 'The Tale of Tom Kitten', 391 author: 'Beatrix Potter', 392 url: 'https://www.gutenberg.org/ebooks/14837', 393 }, 394 blanks: [ 395 { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, 396 { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, 397 { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, 398 { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, 399 ], 400 story: 'Mrs. Tabitha Twitchit had three kittens, and the naughtiest was named {name}. ' + 401 'Company was coming for {food}, so she scrubbed their faces and dressed them in {color} clothes. ' + 402 '"Now keep clean!" she said, and put them out in the garden. ' + 403 'Of course {name} climbed the wall straightaway, and all the buttons popped off — pop, pop, pop. ' + 404 'A passing {animal} tried the clothes on and found them a very good fit. ' + 405 'And that is why the kittens had to stay upstairs when the company came.', 406 }, 407 { 408 id: 'mother-goose', 409 title: 'Hey Diddle Diddle', 410 source: { 411 title: 'Hey Diddle Diddle (The Real Mother Goose)', 412 author: 'compiled by Blanche Fisher Wright', 413 url: 'https://www.gutenberg.org/ebooks/10607', 414 }, 415 blanks: [ 416 { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, 417 { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, 418 { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, 419 { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, 420 ], 421 story: 'Hey diddle diddle, the {animal} and the fiddle, ' + 422 'the cow jumped over {place}! ' + 423 'The little dog laughed to see such fun, and shouted "{silly}!" ' + 424 'And the dish ran away with the spoon — and with {name}, too.', 425 }, 426 ]; 427 428 // ── Pure helpers ───────────────────────────────────────────────────────────── 429 430 function fillTemplate(template, values) { 431 return template.story.replace(/\{(\w+)\}/g, function (_, key) { 432 const v = values[key]; 433 return (v === undefined || v === null || v === '') ? '___' : String(v); 434 }); 435 } 436 437 function pickTemplate(rng) { 438 const r = (typeof rng === 'function') ? rng : Math.random; 439 return MADLIB_TEMPLATES[Math.floor(r() * MADLIB_TEMPLATES.length)]; 440 } 441 442 // Frame-by-frame strings for the live "typing" effect: backspace `from` 443 // down to the common prefix of `from`/`to`, then type up to `to`. 444 function typewriterSteps(from, to) { 445 from = from || ''; 446 to = to || ''; 447 let common = 0; 448 const max = Math.min(from.length, to.length); 449 while (common < max && from[common] === to[common]) common++; 450 451 const steps = []; 452 for (let i = from.length - 1; i >= common; i--) steps.push(from.slice(0, i)); 453 for (let i = common + 1; i <= to.length; i++) steps.push(to.slice(0, i)); 454 if (steps.length === 0 || steps[steps.length - 1] !== to) steps.push(to); 455 return steps; 456 } 457 458 function normalizeName(str) { 459 return String(str == null ? '' : str).trim().replace(/\s+/g, ' ').toLowerCase(); 460 } 461 462 function isNameReady(str) { 463 return normalizeName(str).length >= 2; 464 } 465 466 function formatTime(ms) { 467 return (Math.max(0, ms) / 1000).toFixed(2) + ' s'; 468 } 469 470 function isNewRecord(ms, best) { 471 return best == null || ms < best; 472 } 473 474 const api = { 475 MADLIB_TEMPLATES, 476 fillTemplate, 477 pickTemplate, 478 typewriterSteps, 479 normalizeName, 480 isNameReady, 481 formatTime, 482 isNewRecord, 483 }; 484 if (typeof module !== 'undefined' && module.exports) module.exports = api; 485 root.WorkLib = api; 486 }(typeof globalThis !== 'undefined' ? globalThis : this));