<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://mewsie.world/CoraTOWiki/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3AMonkeyTTracker.js</id>
	<title>MediaWiki:MonkeyTTracker.js - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://mewsie.world/CoraTOWiki/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3AMonkeyTTracker.js"/>
	<link rel="alternate" type="text/html" href="https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:MonkeyTTracker.js&amp;action=history"/>
	<updated>2026-06-20T10:55:27Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:MonkeyTTracker.js&amp;diff=6742&amp;oldid=prev</id>
		<title>Mewsie: monk e</title>
		<link rel="alternate" type="text/html" href="https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:MonkeyTTracker.js&amp;diff=6742&amp;oldid=prev"/>
		<updated>2026-03-26T00:34:59Z</updated>

		<summary type="html">&lt;p&gt;monk e&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 20:34, 25 March 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l6&quot;&gt;Line 6:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 6:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     questCheckbox: &amp;#039;.quest-checkbox&amp;#039;,&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     questCheckbox: &amp;#039;.quest-checkbox&amp;#039;,&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     locationSection: &amp;#039;.location-section&amp;#039;,&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     locationSection: &amp;#039;.location-section&amp;#039;,&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     trackerRootId: &#039;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;tab&lt;/del&gt;-tracker&#039;,&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     trackerRootId: &#039;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;monkeytab&lt;/ins&gt;-tracker&#039;,&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     totalProgress: &amp;#039;#total-progress&amp;#039;,&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     totalProgress: &amp;#039;#total-progress&amp;#039;,&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     completionPercentage: &amp;#039;#completion-percentage&amp;#039;,&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     completionPercentage: &amp;#039;#completion-percentage&amp;#039;,&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key mewsiewo_mcoraw14278-mwct_:diff:1.41:old-6606:rev-6742:php=table --&gt;
&lt;/table&gt;</summary>
		<author><name>Mewsie</name></author>
	</entry>
	<entry>
		<id>https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:MonkeyTTracker.js&amp;diff=6606&amp;oldid=prev</id>
		<title>Noorisei: Created page with &quot;(function () {   &#039;use strict&#039;;    const STORAGE_KEY = &#039;cora.questTracker.monkeyT.v1&#039;;   const SELECTORS = {     questCheckbox: &#039;.quest-checkbox&#039;,     locationSection: &#039;.location-section&#039;,     trackerRootId: &#039;tab-tracker&#039;,     totalProgress: &#039;#total-progress&#039;,     completionPercentage: &#039;#completion-percentage&#039;,     progressFill: &#039;#progress-fill&#039;,     resetButton: &#039;#reset-progress&#039;,     statsContainer: &#039;.progress-stats&#039;   };    function safeQuerySelector(selector, context)...&quot;</title>
		<link rel="alternate" type="text/html" href="https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:MonkeyTTracker.js&amp;diff=6606&amp;oldid=prev"/>
		<updated>2025-12-30T09:21:53Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;(function () {   &amp;#039;use strict&amp;#039;;    const STORAGE_KEY = &amp;#039;cora.questTracker.monkeyT.v1&amp;#039;;   const SELECTORS = {     questCheckbox: &amp;#039;.quest-checkbox&amp;#039;,     locationSection: &amp;#039;.location-section&amp;#039;,     trackerRootId: &amp;#039;tab-tracker&amp;#039;,     totalProgress: &amp;#039;#total-progress&amp;#039;,     completionPercentage: &amp;#039;#completion-percentage&amp;#039;,     progressFill: &amp;#039;#progress-fill&amp;#039;,     resetButton: &amp;#039;#reset-progress&amp;#039;,     statsContainer: &amp;#039;.progress-stats&amp;#039;   };    function safeQuerySelector(selector, context)...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;(function () {&lt;br /&gt;
  &amp;#039;use strict&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
  const STORAGE_KEY = &amp;#039;cora.questTracker.monkeyT.v1&amp;#039;;&lt;br /&gt;
  const SELECTORS = {&lt;br /&gt;
    questCheckbox: &amp;#039;.quest-checkbox&amp;#039;,&lt;br /&gt;
    locationSection: &amp;#039;.location-section&amp;#039;,&lt;br /&gt;
    trackerRootId: &amp;#039;tab-tracker&amp;#039;,&lt;br /&gt;
    totalProgress: &amp;#039;#total-progress&amp;#039;,&lt;br /&gt;
    completionPercentage: &amp;#039;#completion-percentage&amp;#039;,&lt;br /&gt;
    progressFill: &amp;#039;#progress-fill&amp;#039;,&lt;br /&gt;
    resetButton: &amp;#039;#reset-progress&amp;#039;,&lt;br /&gt;
    statsContainer: &amp;#039;.progress-stats&amp;#039;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  function safeQuerySelector(selector, context) {&lt;br /&gt;
    try {&lt;br /&gt;
      return (context || document).querySelector(selector);&lt;br /&gt;
    } catch (_) {&lt;br /&gt;
      return null;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function safeQuerySelectorAll(selector, context) {&lt;br /&gt;
    try {&lt;br /&gt;
      return Array.from((context || document).querySelectorAll(selector));&lt;br /&gt;
    } catch (_) {&lt;br /&gt;
      return [];&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function safeJsonParse(value) {&lt;br /&gt;
    try {&lt;br /&gt;
      return JSON.parse(value);&lt;br /&gt;
    } catch (_) {&lt;br /&gt;
      return null;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function safeGetLocalStorage(key) {&lt;br /&gt;
    try {&lt;br /&gt;
      return localStorage.getItem(key);&lt;br /&gt;
    } catch (_) {&lt;br /&gt;
      return null;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function safeSetLocalStorage(key, value) {&lt;br /&gt;
    try {&lt;br /&gt;
      localStorage.setItem(key, value);&lt;br /&gt;
    } catch (_) {}&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function safeRemoveLocalStorage(key) {&lt;br /&gt;
    try {&lt;br /&gt;
      localStorage.removeItem(key);&lt;br /&gt;
    } catch (_) {}&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function getQuestId(checkbox, index) {&lt;br /&gt;
    const dataQuest = checkbox.getAttribute(&amp;#039;data-quest&amp;#039;);&lt;br /&gt;
    if (dataQuest) return dataQuest;&lt;br /&gt;
    const name = checkbox.getAttribute(&amp;#039;name&amp;#039;);&lt;br /&gt;
    if (name) return name;&lt;br /&gt;
    const id = checkbox.getAttribute(&amp;#039;id&amp;#039;);&lt;br /&gt;
    if (id) return id;&lt;br /&gt;
    return `quest-${index}`;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function parseRequiredCountForCheckbox(checkbox) {&lt;br /&gt;
    const label = checkbox.closest(&amp;#039;label&amp;#039;);&lt;br /&gt;
    const nameEl = label ? safeQuerySelector(&amp;#039;.quest-name&amp;#039;, label) : null;&lt;br /&gt;
    const text = (nameEl ? nameEl.textContent : &amp;#039;&amp;#039;) || &amp;#039;&amp;#039;;&lt;br /&gt;
    const match = text.match(/(\d+)/);&lt;br /&gt;
    if (!match) return 1;&lt;br /&gt;
    const n = Number(match[1]);&lt;br /&gt;
    return Number.isFinite(n) &amp;amp;&amp;amp; n &amp;gt; 0 ? n : 1;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function collectRegions(root) {&lt;br /&gt;
    const sections = safeQuerySelectorAll(SELECTORS.locationSection, root);&lt;br /&gt;
    return sections&lt;br /&gt;
      .map((section) =&amp;gt; ({&lt;br /&gt;
        section,&lt;br /&gt;
        checkboxes: safeQuerySelectorAll(SELECTORS.questCheckbox, section)&lt;br /&gt;
      }))&lt;br /&gt;
      .filter((x) =&amp;gt; x.checkboxes.length &amp;gt; 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function applySingleSelectionPerRegion(regions) {&lt;br /&gt;
    regions.forEach(({ checkboxes }) =&amp;gt; {&lt;br /&gt;
      const checked = checkboxes.filter((cb) =&amp;gt; cb.checked);&lt;br /&gt;
      const anyChecked = checked.length &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
      if (!anyChecked) {&lt;br /&gt;
        checkboxes.forEach((cb) =&amp;gt; {&lt;br /&gt;
          cb.disabled = false;&lt;br /&gt;
        });&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      const chosen = checked[0];&lt;br /&gt;
      if (checked.length &amp;gt; 1) {&lt;br /&gt;
        checked.slice(1).forEach((cb) =&amp;gt; {&lt;br /&gt;
          cb.checked = false;&lt;br /&gt;
        });&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      checkboxes.forEach((cb) =&amp;gt; {&lt;br /&gt;
        cb.disabled = cb !== chosen;&lt;br /&gt;
      });&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function loadState(checkboxes) {&lt;br /&gt;
    const raw = safeGetLocalStorage(STORAGE_KEY);&lt;br /&gt;
    const parsed = raw ? safeJsonParse(raw) : null;&lt;br /&gt;
    const state = parsed &amp;amp;&amp;amp; typeof parsed === &amp;#039;object&amp;#039; ? parsed : {};&lt;br /&gt;
&lt;br /&gt;
    checkboxes.forEach((cb, index) =&amp;gt; {&lt;br /&gt;
      const key = getQuestId(cb, index);&lt;br /&gt;
      cb.checked = !!state[key];&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function saveState(checkboxes) {&lt;br /&gt;
    const out = {};&lt;br /&gt;
    checkboxes.forEach((cb, index) =&amp;gt; {&lt;br /&gt;
      const key = getQuestId(cb, index);&lt;br /&gt;
      out[key] = !!cb.checked;&lt;br /&gt;
    });&lt;br /&gt;
    safeSetLocalStorage(STORAGE_KEY, JSON.stringify(out));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function ensureItemsStatElement(root) {&lt;br /&gt;
    const container = safeQuerySelector(SELECTORS.statsContainer, root);&lt;br /&gt;
    if (!container) return null;&lt;br /&gt;
&lt;br /&gt;
    let el = container.querySelector(&amp;#039;[data-stat=&amp;quot;items-progress&amp;quot;]&amp;#039;);&lt;br /&gt;
    if (el) return el;&lt;br /&gt;
&lt;br /&gt;
    const item = document.createElement(&amp;#039;div&amp;#039;);&lt;br /&gt;
    item.className = &amp;#039;stat-item&amp;#039;;&lt;br /&gt;
    item.setAttribute(&amp;#039;data-stat&amp;#039;, &amp;#039;items-progress&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
    const label = document.createElement(&amp;#039;span&amp;#039;);&lt;br /&gt;
    label.className = &amp;#039;stat-label&amp;#039;;&lt;br /&gt;
    label.textContent = &amp;#039;Items:&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
    const value = document.createElement(&amp;#039;span&amp;#039;);&lt;br /&gt;
    value.className = &amp;#039;stat-value&amp;#039;;&lt;br /&gt;
    value.setAttribute(&amp;#039;data-stat-value&amp;#039;, &amp;#039;items-progress&amp;#039;);&lt;br /&gt;
    value.textContent = &amp;#039;0/0&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
    item.appendChild(label);&lt;br /&gt;
    item.appendChild(value);&lt;br /&gt;
    container.appendChild(item);&lt;br /&gt;
&lt;br /&gt;
    return item;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function updateProgress(regions, allCheckboxes, root) {&lt;br /&gt;
    const totalRegions = regions.length;&lt;br /&gt;
    const completedRegions = regions.filter(({ checkboxes }) =&amp;gt; checkboxes.some((cb) =&amp;gt; cb.checked)).length;&lt;br /&gt;
    const percentage = totalRegions &amp;gt; 0 ? Math.round((completedRegions / totalRegions) * 100) : 0;&lt;br /&gt;
&lt;br /&gt;
    const totalProgressEl = safeQuerySelector(SELECTORS.totalProgress, root);&lt;br /&gt;
    const completionEl = safeQuerySelector(SELECTORS.completionPercentage, root);&lt;br /&gt;
    const fillEl = safeQuerySelector(SELECTORS.progressFill, root);&lt;br /&gt;
&lt;br /&gt;
    if (totalProgressEl) totalProgressEl.textContent = `${completedRegions}/${totalRegions}`;&lt;br /&gt;
    if (completionEl) completionEl.textContent = `${percentage}%`;&lt;br /&gt;
    if (fillEl) fillEl.style.width = `${percentage}%`;&lt;br /&gt;
&lt;br /&gt;
    const itemsStat = ensureItemsStatElement(root);&lt;br /&gt;
    const itemsValue = itemsStat ? itemsStat.querySelector(&amp;#039;[data-stat-value=&amp;quot;items-progress&amp;quot;]&amp;#039;) : null;&lt;br /&gt;
    if (itemsValue) {&lt;br /&gt;
      const totalItems = allCheckboxes.reduce((sum, cb) =&amp;gt; sum + parseRequiredCountForCheckbox(cb), 0);&lt;br /&gt;
      const completedItems = allCheckboxes.reduce(&lt;br /&gt;
        (sum, cb) =&amp;gt; sum + (cb.checked ? parseRequiredCountForCheckbox(cb) : 0),&lt;br /&gt;
        0&lt;br /&gt;
      );&lt;br /&gt;
      itemsValue.textContent = `${completedItems}/${totalItems}`;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function wireEvents(regions, checkboxes, root) {&lt;br /&gt;
    checkboxes.forEach((cb) =&amp;gt; {&lt;br /&gt;
      cb.addEventListener(&amp;#039;change&amp;#039;, () =&amp;gt; {&lt;br /&gt;
        applySingleSelectionPerRegion(regions);&lt;br /&gt;
        saveState(checkboxes);&lt;br /&gt;
        updateProgress(regions, checkboxes, root);&lt;br /&gt;
      });&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    const resetBtn = safeQuerySelector(SELECTORS.resetButton, root);&lt;br /&gt;
    if (resetBtn) {&lt;br /&gt;
      resetBtn.addEventListener(&amp;#039;click&amp;#039;, () =&amp;gt; {&lt;br /&gt;
        checkboxes.forEach((cb) =&amp;gt; {&lt;br /&gt;
          cb.checked = false;&lt;br /&gt;
          cb.disabled = false;&lt;br /&gt;
        });&lt;br /&gt;
        safeRemoveLocalStorage(STORAGE_KEY);&lt;br /&gt;
        updateProgress(regions, checkboxes, root);&lt;br /&gt;
      });&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function init() {&lt;br /&gt;
    const root = document.getElementById(SELECTORS.trackerRootId);&lt;br /&gt;
    if (!root) return;&lt;br /&gt;
&lt;br /&gt;
    const checkboxes = safeQuerySelectorAll(SELECTORS.questCheckbox, root);&lt;br /&gt;
    if (checkboxes.length === 0) return;&lt;br /&gt;
&lt;br /&gt;
    const regions = collectRegions(root);&lt;br /&gt;
    if (regions.length === 0) return;&lt;br /&gt;
&lt;br /&gt;
    loadState(checkboxes);&lt;br /&gt;
    applySingleSelectionPerRegion(regions);&lt;br /&gt;
    saveState(checkboxes);&lt;br /&gt;
    updateProgress(regions, checkboxes, root);&lt;br /&gt;
    wireEvents(regions, checkboxes, root);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (document.readyState === &amp;#039;loading&amp;#039;) {&lt;br /&gt;
    document.addEventListener(&amp;#039;DOMContentLoaded&amp;#039;, init);&lt;br /&gt;
  } else {&lt;br /&gt;
    init();&lt;br /&gt;
  }&lt;br /&gt;
})();&lt;/div&gt;</summary>
		<author><name>Noorisei</name></author>
	</entry>
</feed>