From: Devin Prater <r.d.t.prater@gmail.com>
To: Richard Lawrence <wyley.r@gmail.com>
Cc: Phil Regier <phil.regier@gmail.com>,
emacs-orgmode <emacs-orgmode@gnu.org>
Subject: Re: Get Grades Done: the joys of Org's simple power
Date: Sun, 14 Jun 2020 11:05:24 -0500 [thread overview]
Message-ID: <CE273E08-2EEC-4D75-963B-3E0B295AC5EB@gmail.com> (raw)
In-Reply-To: <871rmic94x.fsf@aquinas>
I tried that on my file, but the checkboxes didn’t update. I’ll give you the kind of file I’m working with:
#+title: Performance test
#+begin_export html
<script type="text/javascript">
function updateCookiesIn(div) {
const headline = div.querySelector("h1, h2, h3, h4, h5, h6");
if (!headline) return;
const cookies = Array.from(headline.querySelectorAll("code"))
.filter(c => c.innerText.startsWith("[") && c.innerText.endsWith("]"));
const fracCookies = cookies.filter(c => c.innerText.includes("/"));
const pctCookies = cookies.filter(c => c.innerText.includes("%"));
// The ugly query strings here restrict the selection to checkboxes at *this* level of the hierarchy
const allTasks = div.querySelectorAll(`#${div.id} > div > ul input[type=checkbox], #${div.id} > div > ol input[type=checkbox]`);
const completedTasks = div.querySelectorAll(`#${div.id} > div > ul input[type=checkbox]:checked, #${div.id} > div > ol input[type=checkbox]:checked`);
const newFrac = `[${completedTasks.length}/${allTasks.length}]`;
const newPctText = allTasks.length
? (100 * completedTasks.length / allTasks.length).toFixed(0)
: "100"; // Org shows 100% for a cookie when there are no checkboxes
const newPct = `[${newPctText}%]`;
fracCookies.forEach(c => c.innerText = newFrac);
pctCookies.forEach(c => c.innerText = newPct);
}
function replaceWithCheckbox(code) {
const isChecked = code.innerText.includes("X");
const checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
if (isChecked) checkbox.setAttribute("checked", "checked");
checkbox.onclick = function (e) {
const container = findContainingSection(e.target);
if (!container) return;
updateCookiesIn(container);
};
code.replaceWith(checkbox);
}
function findContainingSection(el) {
if (!el.parentElement) return null;
const parent = el.parentElement;
const classes = Array.from(parent.classList);
if (classes.some(cl => cl.startsWith("outline") && !cl.startsWith("outline-text"))) {
return parent;
} else {
return findContainingSection(parent);
}
}
const orgCheckboxes = document.querySelectorAll(".off > code, .on > code");
orgCheckboxes.forEach(replaceWithCheckbox);
const orgSections = document.querySelectorAll("div.outline-1, div.outline-2, div.outline-3, div.outline-4, div.outline-5, div.outline-6");
orgSections.forEach(updateCookiesIn);
</script>
#+end_export
* Performance test
** Student Name [0/10] [0%]
Date
The student will perform the following features:
1. [ ]
2. [ ]
3. [ ]
4. [ ]
5. [ ]
6. [ ]
7. [ ]
8. [ ]
9. [ ]
10. [ ]
When I do C-E h o, and check one of the boxes, the grade isn’t updated.
> On Jun 14, 2020, at 4:02 AM, Richard Lawrence <wyley.r@gmail.com> wrote:
>
> Hi Devin and all,
>
> Devin Prater <r.d.t.prater@gmail.com> writes:
>
>> Yeah, I was hoping to just have an HTML page or something that could
>> be put on Github or just sent in an email attachment, where checking
>> checkboxes would update the fraction cookie.
>
> I hacked together a quick solution for this. You should be able to just
> drop this Javascript into an Org file. When it is included in HTML that
> has been exported via the standard Org exporter, it replaces the static
> "[ ]" and "[X]" code elements with actual HTML checkboxes, and updates
> any progress cookies in the relevant headlines when those boxes are
> checked or unchecked.
>
> I haven't tested it extensively, and the code can surely be improved,
> but it works for the cases I could think to test.
>
> #+begin_export html
> <script type="text/javascript">
> function updateCookiesIn(div) {
> const headline = div.querySelector("h1, h2, h3, h4, h5, h6");
> if (!headline) return;
> const cookies = Array.from(headline.querySelectorAll("code"))
> .filter(c => c.innerText.startsWith("[") && c.innerText.endsWith("]"));
> const fracCookies = cookies.filter(c => c.innerText.includes("/"));
> const pctCookies = cookies.filter(c => c.innerText.includes("%"));
>
> // The ugly query strings here restrict the selection to checkboxes at *this* level of the hierarchy
> const allTasks = div.querySelectorAll(`#${div.id} > div > ul input[type=checkbox], #${div.id} > div > ol input[type=checkbox]`);
> const completedTasks = div.querySelectorAll(`#${div.id} > div > ul input[type=checkbox]:checked, #${div.id} > div > ol input[type=checkbox]:checked`);
>
> const newFrac = `[${completedTasks.length}/${allTasks.length}]`;
> const newPctText = allTasks.length
> ? (100 * completedTasks.length / allTasks.length).toFixed(0)
> : "100"; // Org shows 100% for a cookie when there are no checkboxes
> const newPct = `[${newPctText}%]`;
>
> fracCookies.forEach(c => c.innerText = newFrac);
> pctCookies.forEach(c => c.innerText = newPct);
> }
>
> function replaceWithCheckbox(code) {
> const isChecked = code.innerText.includes("X");
>
> const checkbox = document.createElement("input");
> checkbox.setAttribute("type", "checkbox");
> if (isChecked) checkbox.setAttribute("checked", "checked");
> checkbox.onclick = function (e) {
> const container = findContainingSection(e.target);
> if (!container) return;
> updateCookiesIn(container);
> };
>
> code.replaceWith(checkbox);
> }
>
> function findContainingSection(el) {
> if (!el.parentElement) return null;
>
> const parent = el.parentElement;
> const classes = Array.from(parent.classList);
> if (classes.some(cl => cl.startsWith("outline") && !cl.startsWith("outline-text"))) {
> return parent;
> } else {
> return findContainingSection(parent);
> }
> }
>
> const orgCheckboxes = document.querySelectorAll(".off > code, .on > code");
> orgCheckboxes.forEach(replaceWithCheckbox);
>
> const orgSections = document.querySelectorAll("div.outline-1, div.outline-2, div.outline-3, div.outline-4, div.outline-5, div.outline-6");
> orgSections.forEach(updateCookiesIn);
> </script>
> #+end_export
>
> Hope that helps!
>
> --
> Best,
> Richard
next prev parent reply other threads:[~2020-06-14 16:06 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-06-10 20:38 Get Grades Done: the joys of Org's simple power Devin Prater
2020-06-11 3:22 ` Russell Adams
2020-06-11 3:55 ` George Mauer
2020-06-11 5:47 ` Steven Harris
2020-06-12 23:23 ` Phil Regier
2020-06-13 0:22 ` Devin Prater
2020-06-13 2:17 ` Phil Regier
2020-06-13 2:45 ` Devin Prater
2020-06-14 9:02 ` Richard Lawrence
2020-06-14 16:05 ` Devin Prater [this message]
2020-06-14 16:59 ` Richard Lawrence
2020-06-14 20:53 ` Devin Prater
2020-06-20 4:10 ` Richard Lawrence
2020-09-03 10:09 ` Bastien
2020-06-11 5:36 ` Diego Zamboni
2020-06-12 15:54 ` Leo Okawa Ericson
2020-06-12 17:01 ` Diego Zamboni
2020-06-13 9:10 ` Leo Okawa Ericson
2020-06-13 11:16 ` Diego Zamboni
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.orgmode.org/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CE273E08-2EEC-4D75-963B-3E0B295AC5EB@gmail.com \
--to=r.d.t.prater@gmail.com \
--cc=emacs-orgmode@gnu.org \
--cc=phil.regier@gmail.com \
--cc=wyley.r@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).