emacs-orgmode
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Get Grades Done: the joys of Org's simple power


From: Richard Lawrence
Subject: Re: Get Grades Done: the joys of Org's simple power
Date: Sun, 14 Jun 2020 11:02:54 +0200

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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]