{"_id":"@creaditor/cdtr-quiz-player","_rev":"5-6cb0622cf6ce4ebcf9e7c19658b9cf9a","name":"@creaditor/cdtr-quiz-player","dist-tags":{"latest":"0.6.0"},"versions":{"0.0.0":{"name":"@creaditor/cdtr-quiz-player","version":"0.0.0","license":"UNLICENSED","_id":"@creaditor/cdtr-quiz-player@0.0.0","maintainers":[{"name":"creaditor","email":"creaditor.system@gmail.com"}],"dist":{"shasum":"beb52bca34e3a37a45afd74ada2f6eca0af9c80f","tarball":"https://registry.npmjs.org/@creaditor/cdtr-quiz-player/-/cdtr-quiz-player-0.0.0.tgz","fileCount":2,"integrity":"sha512-2yME8+HVUpdsV5yHUx6c/qigroSWNZ4VipRp8BGjIexgsnFLqCiQOmgeQ7UWzVd6wcBy3ysEIoRoW9Q84OmIyw==","signatures":[{"sig":"MEQCIAfPknvTc5bC0E8S/l05TFeR+atqr6f8sAtoqk5ZQt1LAiAA2HKTRgsPJZ//YLLJUNH5G3IaF6lxYnJQVoJqKxEjfA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":377},"_npmUser":{"name":"creaditor","email":"creaditor.system@gmail.com"},"_npmVersion":"11.3.0","description":"Name placeholder. Real release to follow.","directories":{},"_nodeVersion":"24.1.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/cdtr-quiz-player_0.0.0_1788391128918_0.44887490438057465","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@creaditor/cdtr-quiz-player","version":"0.4.0","keywords":["web-components","creaditor","course","quiz","lms","drop-in","hebrew","rtl"],"author":{"name":"Creaditor"},"license":"MIT","_id":"@creaditor/cdtr-quiz-player@0.4.0","maintainers":[{"name":"creaditor","email":"creaditor.system@gmail.com"},{"name":"adidui","email":"adilev3344@gmail.com"},{"name":"iyris","email":"iyrisvigil@gmail.com"}],"dist":{"shasum":"16a3966c458cbec1e0044f4cd23582dc3f049d36","tarball":"https://registry.npmjs.org/@creaditor/cdtr-quiz-player/-/cdtr-quiz-player-0.4.0.tgz","fileCount":9,"integrity":"sha512-2rfeTCGNL9yw/SZ6BoJ+RehcAl2uRIPcP3W+a3HcH2LEIhvrW9AXp6k3l6QIZyaWkxxHFF3lbmrwRhpQU8/k9g==","signatures":[{"sig":"MEUCIDICYHx2IXH9kXaGqy6qhmqLI3RQzHYN/OoWhEpDSW3OAiEAtzEb+XrlwIjmbsVK87rsFaIE03RcNgY/z9XKink/4Fk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":51311},"main":"./dist/cdtr-quiz-player.es.js","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/cdtr-quiz-player.umd.js","module":"./dist/cdtr-quiz-player.es.js","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/cdtr-quiz-player.es.js"},"./package.json":"./package.json"},"gitHead":"cfdeaa705db17283b65b1d0b0ef5bb257bdc9f6d","private":false,"scripts":{"dev":"vite","test":"vitest run","build":"vite build && tsc -p tsconfig.build.json","prepack":"npm run build","preview":"vite preview"},"_npmUser":{"name":"iyris","email":"iyrisvigil@gmail.com"},"jsdelivr":"./dist/cdtr-quiz-player.umd.js","_npmVersion":"11.17.0","description":"Embeddable <cdtr-quiz-player> web component. Draws a quiz authored in cdtr-course-builder, marks each answer, and grades the finished attempt against a passing mark. No networking, no storage, no identity: the host owns the gradebook and what a result mea","directories":{},"_nodeVersion":"26.5.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.2.0","vitest":"^4.1.7","happy-dom":"^20.8.9","typescript":"~5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/cdtr-quiz-player_0.4.0_1788392321592_0.14274415464178203","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@creaditor/cdtr-quiz-player","version":"0.5.0","keywords":["web-components","creaditor","course","quiz","lms","drop-in","hebrew","rtl"],"author":{"name":"Creaditor"},"license":"MIT","_id":"@creaditor/cdtr-quiz-player@0.5.0","maintainers":[{"name":"creaditor","email":"creaditor.system@gmail.com"},{"name":"adidui","email":"adilev3344@gmail.com"},{"name":"iyris","email":"iyrisvigil@gmail.com"}],"dist":{"shasum":"fbef4eac3f7f398425734b411660fcfab38010e9","tarball":"https://registry.npmjs.org/@creaditor/cdtr-quiz-player/-/cdtr-quiz-player-0.5.0.tgz","fileCount":9,"integrity":"sha512-teKqttt7eqw5aBddl47ma5+CpO1/u1puvvasfhvmexq0hnxwtzJhA4Y410ObVWsxyPHUuYfSfCyAMaDOdspjaA==","signatures":[{"sig":"MEUCIQCPKCewPus2Sh78bvRMvneahVOir8F/7FZRKutimswPPwIgbtJV/S9Ydpd5EY/NYL/bxsC39Jj/j7bzs1KvPnp2bVw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIH12sIRmPCCe7WyYmvrvTFJijXbtCliA+Dawifxl5EC+AiEApLjWqexlUVNEDQ+pyHi/gEPUe2KV/7CP3dc1nvZOGPA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":60705},"main":"./dist/cdtr-quiz-player.es.js","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/cdtr-quiz-player.umd.js","module":"./dist/cdtr-quiz-player.es.js","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/cdtr-quiz-player.es.js"},"./package.json":"./package.json"},"gitHead":"ac67b3eb0820ca9f7b0ffb225f30d908aef45513","private":false,"scripts":{"dev":"vite","test":"vitest run","build":"vite build && tsc -p tsconfig.build.json","prepack":"npm run build","preview":"vite preview"},"_npmUser":{"name":"iyris","email":"iyrisvigil@gmail.com"},"jsdelivr":"./dist/cdtr-quiz-player.umd.js","_npmVersion":"11.17.0","description":"Embeddable <cdtr-quiz-player> web component. Draws a quiz authored in cdtr-course-builder, marks each answer, and grades the finished attempt against a passing mark. No networking, no storage, no identity: the host owns the gradebook and what a result mea","directories":{},"_nodeVersion":"26.5.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.2.0","vitest":"^4.1.7","happy-dom":"^20.8.9","typescript":"~5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/cdtr-quiz-player_0.5.0_1789560979745_0.7071565925950196","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"_id":"@creaditor/cdtr-quiz-player@0.6.0","dist":{"shasum":"c4e8c6ab00246d8433e7b8cda801d9245821f3e8","tarball":"https://registry.npmjs.org/@creaditor/cdtr-quiz-player/-/cdtr-quiz-player-0.6.0.tgz","fileCount":9,"integrity":"sha512-vKuCkkU2bcrW5B47EM1Zu3UkUtnDjuFtPYM5vjnNwmGM0UUSwHBtmv1T3tF95Hi5E5CFjw9NcUZt0kSBJZabaQ==","signatures":[{"sig":"MEUCIFA0Ho9JOErXwbzzkEsrzj3KbpoVMljf/x0e8Dvb0tlWAiEAgMzoXADMtgpB8HUE2M/pkpfvjiBm+v/ri7uUeFng/24=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDhsHnxxHCb8fH0FEhfsc94bkK2tQQSkXKAQY9V7yBVLAIhANgCEzqC3p5h8KDO9/Ce3vAUVI6R//vZ0P9uyQAQavMX"}],"unpackedSize":67075},"main":"./dist/cdtr-quiz-player.es.js","name":"@creaditor/cdtr-quiz-player","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/cdtr-quiz-player.umd.js","author":{"name":"Creaditor"},"module":"./dist/cdtr-quiz-player.es.js","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/cdtr-quiz-player.es.js"},"./package.json":"./package.json"},"gitHead":"fd398329a79eb1f2a50337ab294cc1c1fc618cf2","license":"MIT","private":false,"scripts":{"dev":"vite","test":"vitest run","build":"vite build && tsc -p tsconfig.build.json","prepack":"npm run build","preview":"vite preview"},"version":"0.6.0","_npmUser":{"name":"iyris","email":"iyrisvigil@gmail.com"},"jsdelivr":"./dist/cdtr-quiz-player.umd.js","keywords":["web-components","creaditor","course","quiz","lms","drop-in","hebrew","rtl"],"_npmVersion":"11.17.0","description":"Embeddable <cdtr-quiz-player> web component. Draws a quiz authored in cdtr-course-builder, marks each answer, and grades the finished attempt against a passing mark. No networking, no storage, no identity: the host owns the gradebook and what a result mea","directories":{},"maintainers":[{"name":"creaditor","email":"creaditor.system@gmail.com"},{"name":"adidui","email":"adilev3344@gmail.com"},{"name":"iyris","email":"iyrisvigil@gmail.com"}],"_nodeVersion":"26.5.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.2.0","vitest":"^4.1.7","happy-dom":"^20.8.9","typescript":"~5.7.2"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/cdtr-quiz-player_0.6.0_1789847055328_0.6526594774441494"}}},"time":{"created":"2026-09-02T23:18:48.768Z","modified":"2026-09-19T19:44:15.590Z","0.0.0":"2026-09-02T23:18:49.088Z","0.4.0":"2026-09-02T23:38:41.742Z","0.5.0":"2026-09-16T12:16:19.830Z","0.6.0":"2026-09-19T19:44:15.423Z"},"author":{"name":"Creaditor"},"license":"MIT","keywords":["web-components","creaditor","course","quiz","lms","drop-in","hebrew","rtl"],"description":"Embeddable <cdtr-quiz-player> web component. Draws a quiz authored in cdtr-course-builder, marks each answer, and grades the finished attempt against a passing mark. No networking, no storage, no identity: the host owns the gradebook and what a result mea","maintainers":[{"name":"creaditor","email":"creaditor.system@gmail.com"},{"name":"adidui","email":"adilev3344@gmail.com"},{"name":"iyris","email":"iyrisvigil@gmail.com"}],"readme":"# @creaditor/cdtr-quiz-player\n\nThe learner's half of a quiz authored in\n[`@creaditor/cdtr-course-builder`](../cdtr-course-builder). It reads the stored\nquiz shape, draws the questions, accepts one answer each and says whether it was\nright.\n\nIt does not fetch, store, or know who the learner is. Every answer leaves as a\n`quiz-answered` event, and the finished attempt leaves as `quiz-completed` with\na grade. That is the same division the builder uses: the component owns the\nsurface, the host owns the data.\n\n```html\n<script type=\"module\" src=\"https://unpkg.com/@creaditor/cdtr-quiz-player\"></script>\n\n<cdtr-quiz-player language=\"he\"></cdtr-quiz-player>\n\n<script type=\"module\">\n  const player = document.querySelector('cdtr-quiz-player');\n\n  // The `props` of a `type: \"quiz\"` node, straight out of save-lesson-data.\n  player.questions = quizNode.props;\n\n  player.addEventListener('quiz-answered', (e) => {\n    // { questionId, optionId, result: 'correct' | 'incorrect' | 'ungraded' }\n    console.log(e.detail);\n  });\n</script>\n```\n\n## What it takes\n\n| | |\n|---|---|\n| `player.questions = …` | A quiz node's `props`, or the bare `questions` array. Setting it clears any answers already given. |\n| `player.props = …` | Alias, for hosts that spread a node's `props`. |\n| `language=\"he\" \\| \"en\"` | Learner-facing copy and text direction. **Defaults to Hebrew**, matching the builder. |\n| `player.questions` (read) | The parsed, drawable questions. |\n| `player.dropped` (read) | How many authored questions were too malformed to draw. |\n| `passing-grade=\"70\"` | The mark, 0–100, the learner must reach. Absent means a score and no verdict. |\n| `display-mode=\"all-at-once\" \\| \"one-at-a-time\"` | Every question on one page, or one at a time. **Defaults to `all-at-once`**, and so does anything unrecognised. |\n| `player.score` (read) | The running `QuizScore`. Readable mid-attempt. |\n| `player.passingGrade` (read) | The parsed mark, or `null`. |\n| `player.displayMode` (read) | The parsed layout mode. |\n| `props.intro` | Instructions shown on their own screen before the questions. Optional. |\n| `props.showAnswers` | `false` hides the right answer after a wrong one. Defaults to showing. |\n| `kind: \"open\"` on a question | The learner writes an answer instead of picking one. Never graded. |\n\n## What it emits\n\nBoth events `bubble` and are `composed`, so a host can delegate from a container.\n\n**`quiz-answered`**, once per question — answering is one-shot, and the question\nlocks afterwards:\n\n```js\n{ questionId: 'q_48c9574f', optionIds: ['o_178416ec'], result: 'correct' }\n```\n\n`optionIds` is always an array — one entry for a single-answer question — so a\nhost reading the event never has to branch on the question type.\n\n`result: 'ungraded'` means the authored question marked no correct answer at\nall; the learner could still pick, and no verdict was shown.\n\n## Choose all that apply\n\nMark more than one option `correct: true` and the question becomes a\nmultiple-answer one on its own. No extra flag:\n\n- The learner gets **checkboxes** instead of radios, and a \"choose all that\n  apply\" line.\n- It is marked right for **exactly** the correct set. No partial credit: half\n  of a set is a different answer, not most of the right one, and awarding it\n  half a mark is a grading policy the creator never set.\n- After answering, **every** right option is marked, not only the one picked —\n  the learner needs to see the whole set they were aiming at.\n- It counts as **one** gradable question, however many boxes it has.\n\nThe input type does tell a learner whether to look for one answer or several.\nThat is a non-issue here for the reason stated under *What this is not*: the\ncorrect answers are already in page memory.\n\n**`quiz-completed`**, once, when the last question is answered — after that\nquestion's own `quiz-answered`, so a host sees them in the order the learner\nlived them:\n\n```js\n{\n  answered: 4, total: 4,      // every drawn question\n  gradable: 3, correct: 3,    // of those, the ones that CAN be scored\n  percent: 100,               // correct out of gradable\n  complete: true,\n  passed: true,               // null when there is no verdict to give\n  passingGrade: 70,\n  answers: [ /* every pick, in the order the questions were drawn */ ]\n}\n```\n\n## One question at a time\n\n`display-mode=\"one-at-a-time\"` shows a single question, a \"Question 3 of 8\"\ncounter, and a Next button that appears once the question has been answered.\n\n```js\nplayer.setAttribute('display-mode', 'one-at-a-time');\n```\n\nIt is the creator's layout choice, not the learner's, and it changes nothing a\nhost sees: the same `quiz-answered` per question, the same `quiz-completed`\nafter the last one, the same score.\n\n- **Forward only.** Answering is one-shot — the inputs lock as soon as a\n  question is checked — so a Back button would offer nothing to do but re-read,\n  while implying an edit that is not on offer.\n- **Next appears only after the question is answered**, and never on the last\n  one, where the summary is what follows.\n- **Feedback still lands per question.** *When* the verdict appears is a\n  separate policy from *how many questions are on screen*; both modes check one\n  question at a time.\n- **Anything unrecognised means `all-at-once`.** Forgiving in the direction that\n  keeps content visible: a misspelt attribute shows the whole quiz, rather than\n  windowing it to one question and looking like content that went missing.\n- **Focus follows the learner** — to the verdict after Check, to the new\n  question after Next. Every action repaints the shadow tree, so without it the\n  keyboard would fall to the body and Next would be reachable only by tabbing in\n  from the top of your page.\n\n## Instructions before the questions\n\nIf the quiz node's `props` carry an `intro` string, the player opens on it —\nthe text, and a Start button — instead of the first question.\n\n```js\nplayer.questions = { questions: [...], intro: 'Read carefully — you get one attempt.' };\n```\n\n- **Only when the creator wrote one.** No `intro`, whitespace-only, or a\n  non-string, and there is no screen: the quiz opens on question 1 exactly as it\n  did before this existed.\n- **It replaces the questions, it does not sit above them.** Pressing Start is\n  what makes it an acknowledgement rather than something scrolled past.\n- **Both display modes.** `one-at-a-time` shows it before the counter.\n- **Never enforced.** Start records that the learner pressed a button, nothing\n  more. \"You get one attempt\" is a rule only you can hold them to, because only\n  you know who is watching.\n- **Not persisted.** A reload shows it again, the same way a reload already\n  returns a `one-at-a-time` learner to question 1.\n\nSetting `questions` again — a host swapping lessons — makes the instructions\nunread again, for the same reason it clears the answers.\n\n## Grading a whole attempt\n\nSet `passing-grade` and the player scores the attempt, shows the learner where\nthey landed, and reports it. **It still decides nothing**: whether a failed quiz\nblocks the lesson, allows a retry, or notifies a teacher is the host's rule.\n\n```js\nplayer.setAttribute('passing-grade', '70');\n\nplayer.addEventListener('quiz-completed', (e) => {\n  myBackend.saveGrade(lessonId, e.detail.percent);      // YOUR gradebook\n  if (e.detail.passed) markLessonComplete(lessonId);    // YOUR rule\n});\n```\n\nFour things it is deliberate about:\n\n- **`gradable` is separate from `total`.** A question naming no single correct\n  answer cannot be scored, so it is kept out of the denominator — otherwise a\n  flawless learner reads 3/4 because of the creator's authoring mistake. It\n  still draws, still takes a pick, and still has to be answered before the\n  attempt is complete.\n- **`passed` is `null`, not `false`, until there is a verdict to give**: no pass\n  mark set, nothing gradable, or the attempt unfinished. A quiz in progress has\n  not been failed.\n- **A pass mark is a percentage, not a question count.** `70%` survives the\n  creator adding a seventh question; \"5 of 6\" quietly becomes wrong.\n- **A quiz with nothing gradable says so in words** rather than showing 0%,\n  which a learner reads as \"I got everything wrong\".\n\nThe mark is read as a percentage 0–100. Anything else — blank, unparseable, out\nof range — means no pass mark rather than a mark of zero.\n\n## The shape it reads\n\nDocumented in full under **The stored quiz shape** in the builder's README. The\npart this package binds to:\n\n```json\n{\n  \"type\": \"quiz\",\n  \"props\": {\n    \"questions\": [\n      {\n        \"id\": \"q_48c9574f\",\n        \"text\": \"What is the capital of France?\",\n        \"options\": [\n          { \"id\": \"o_959bda38\", \"text\": \"Lyon\",  \"correct\": false },\n          { \"id\": \"o_178416ec\", \"text\": \"Paris\", \"correct\": true  }\n        ]\n      }\n    ]\n  }\n}\n```\n\nThat JSON is the coupling between the two packages — not a TypeScript import.\nThis package depends on nothing at all, which is what lets it drop into a\nrenderer without dragging an authoring tool and its editor bundle behind it.\n\n## It expects to be handed broken data\n\nThe builder enforces 2–6 options, exactly one correct, and non-blank text — but\nonly while a creator is typing, and explicitly not on data that reached it from\nsomewhere else. By the time a quiz arrives here it has been through a database\nand a network hop, so every field is read defensively:\n\n- A question that cannot be drawn is **counted in `dropped`, never thrown**.\n- Malformed options are dropped individually; the question survives if two\n  usable ones remain.\n- Duplicate question or option ids collapse — answers are reported by id, and\n  duplicates would make them unattributable.\n- `correct` must be exactly `true`. A stringified `\"false\"` from a database\n  round-trip does not mark an option correct.\n- **Zero** correct answers is the only ungradable state, and the question still\n  draws rather than hiding the creator's work from a learner. Two or more is a\n  choose-all-that-apply question, not a broken one.\n\n`parseQuiz`, `gradeAnswer`, `scoreQuiz` and `readPassingGrade` are exported if\nyou want the logic without the element — `scoreQuiz` is what a host would call\nto grade server-side against answers it collected itself. They are pure, and\ntheir tests run with no DOM in scope.\n\n## What this is not\n\n**Not an assessment.** The verdict is computed in the browser, so the correct\nanswer is on the client and anyone can read it out of memory. That is fine for\npractice and formative feedback. A grade that counts has to be scored on a\nserver against answers the client never sees — a different contract, deliberately\nnot attempted here.\n\nA score is now computed across questions, and it is still not an assessment for\nthe reason above: the marking happens on the client.\n\nAlso absent, and belonging to a host or a later milestone: retries, saving a\ngrade, one-question-at-a-time reveal, and anything that remembers a learner\nbetween visits.\n\n## Development\n\n```bash\nnpm install\nnpm test          # 105 tests, node + happy-dom in one run\nnpm run dev       # preview.html on :5182\nnpm run build\n```\n\n`preview.html` carries three fixtures — a well-formed quiz, deliberately\nhalf-broken data, and an empty one — plus a live log of every event the\ncomponent emits.\n","readmeFilename":"README.md"}