<?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%3ADailyQuestTracker.js</id>
	<title>MediaWiki:DailyQuestTracker.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%3ADailyQuestTracker.js"/>
	<link rel="alternate" type="text/html" href="https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:DailyQuestTracker.js&amp;action=history"/>
	<updated>2026-05-05T02:22:44Z</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:DailyQuestTracker.js&amp;diff=6604&amp;oldid=prev</id>
		<title>Noorisei: Noorisei moved page MediaWiki:QuestTracker.js to MediaWiki:DailyQuestTracker.js</title>
		<link rel="alternate" type="text/html" href="https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:DailyQuestTracker.js&amp;diff=6604&amp;oldid=prev"/>
		<updated>2025-12-30T09:20:46Z</updated>

		<summary type="html">&lt;p&gt;Noorisei moved page &lt;a href=&quot;/CoraTOWiki/index.php?title=MediaWiki:QuestTracker.js&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;MediaWiki:QuestTracker.js (page does not exist)&quot;&gt;MediaWiki:QuestTracker.js&lt;/a&gt; to &lt;a href=&quot;/CoraTOWiki/index.php/MediaWiki:DailyQuestTracker.js&quot; title=&quot;MediaWiki:DailyQuestTracker.js&quot;&gt;MediaWiki:DailyQuestTracker.js&lt;/a&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 05:20, 30 December 2025&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;en&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Noorisei</name></author>
	</entry>
	<entry>
		<id>https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:DailyQuestTracker.js&amp;diff=6603&amp;oldid=prev</id>
		<title>Noorisei at 09:20, 30 December 2025</title>
		<link rel="alternate" type="text/html" href="https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:DailyQuestTracker.js&amp;diff=6603&amp;oldid=prev"/>
		<updated>2025-12-30T09:20:23Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;a href=&quot;https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:DailyQuestTracker.js&amp;amp;diff=6603&amp;amp;oldid=6445&quot;&gt;Show changes&lt;/a&gt;</summary>
		<author><name>Noorisei</name></author>
	</entry>
	<entry>
		<id>https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:DailyQuestTracker.js&amp;diff=6445&amp;oldid=prev</id>
		<title>Noorisei: Created page with &quot; &#039;use strict&#039;;  (function() {   function safeGetElementById(id) {     try { return document.getElementById(id); } catch (_) { return null; }   }   function safeQuerySelectorAll(selector, context = document) {     try { return context.querySelectorAll(selector); } catch (_) { return []; }   }   function safeQuerySelector(selector, context = document) {     try { return context.querySelector(selector); } catch (_) { return null; }   }   function safeGetLocalStorage(key, de...&quot;</title>
		<link rel="alternate" type="text/html" href="https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:DailyQuestTracker.js&amp;diff=6445&amp;oldid=prev"/>
		<updated>2025-12-17T07:16:15Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot; &amp;#039;use strict&amp;#039;;  (function() {   function safeGetElementById(id) {     try { return document.getElementById(id); } catch (_) { return null; }   }   function safeQuerySelectorAll(selector, context = document) {     try { return context.querySelectorAll(selector); } catch (_) { return []; }   }   function safeQuerySelector(selector, context = document) {     try { return context.querySelector(selector); } catch (_) { return null; }   }   function safeGetLocalStorage(key, de...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt; &amp;#039;use strict&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
(function() {&lt;br /&gt;
  function safeGetElementById(id) {&lt;br /&gt;
    try { return document.getElementById(id); } catch (_) { return null; }&lt;br /&gt;
  }&lt;br /&gt;
  function safeQuerySelectorAll(selector, context = document) {&lt;br /&gt;
    try { return context.querySelectorAll(selector); } catch (_) { return []; }&lt;br /&gt;
  }&lt;br /&gt;
  function safeQuerySelector(selector, context = document) {&lt;br /&gt;
    try { return context.querySelector(selector); } catch (_) { return null; }&lt;br /&gt;
  }&lt;br /&gt;
  function safeGetLocalStorage(key, defaultValue = &amp;#039;&amp;#039;) {&lt;br /&gt;
    try { return localStorage.getItem(key) || defaultValue; } catch (_) { return defaultValue; }&lt;br /&gt;
  }&lt;br /&gt;
  function safeSetLocalStorage(key, value) {&lt;br /&gt;
    try { localStorage.setItem(key, value); return true; } catch (_) { return false; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const STORAGE_KEY = &amp;#039;shamanJiaQuestProgress&amp;#039;;&lt;br /&gt;
  const SELECTORS = {&lt;br /&gt;
    QUEST_CHECKBOX: &amp;#039;.quest-checkbox&amp;#039;,&lt;br /&gt;
    REGION_CHECKBOX: &amp;#039;.region-checkbox&amp;#039;,&lt;br /&gt;
    LOCATION_SECTION: &amp;#039;.location-section&amp;#039;,&lt;br /&gt;
    LOCATION_TITLE: &amp;#039;.location-title&amp;#039;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  const QuestTracker = {&lt;br /&gt;
    initialized: false,&lt;br /&gt;
    init: function() {&lt;br /&gt;
      if (this.initialized) return;&lt;br /&gt;
      const questCheckboxes = safeQuerySelectorAll(SELECTORS.QUEST_CHECKBOX);&lt;br /&gt;
      if (!questCheckboxes || questCheckboxes.length === 0) return;&lt;br /&gt;
      this.registerRegions();&lt;br /&gt;
      this.loadQuestProgress();&lt;br /&gt;
      this.setupEventListeners();&lt;br /&gt;
      this.updateProgressDisplay();&lt;br /&gt;
      this.initialized = true;&lt;br /&gt;
    },&lt;br /&gt;
    registerRegions: function() {&lt;br /&gt;
      const sections = safeQuerySelectorAll(SELECTORS.LOCATION_SECTION);&lt;br /&gt;
      Array.from(sections).forEach(section =&amp;gt; {&lt;br /&gt;
        const titleEl = safeQuerySelector(SELECTORS.LOCATION_TITLE, section);&lt;br /&gt;
        const name = titleEl ? titleEl.textContent.trim() : &amp;#039;&amp;#039;;&lt;br /&gt;
        const regionId = name ? name.toLowerCase().replace(/\s+/g, &amp;#039;-&amp;#039;) : &amp;#039;&amp;#039;;&lt;br /&gt;
        const cbs = safeQuerySelectorAll(SELECTORS.QUEST_CHECKBOX, section);&lt;br /&gt;
        Array.from(cbs).forEach(cb =&amp;gt; { if (regionId) cb.setAttribute(&amp;#039;data-region&amp;#039;, regionId); });&lt;br /&gt;
        const rc = safeQuerySelector(SELECTORS.REGION_CHECKBOX, section);&lt;br /&gt;
        if (rc &amp;amp;&amp;amp; regionId) rc.setAttribute(&amp;#039;data-region&amp;#039;, regionId);&lt;br /&gt;
      });&lt;br /&gt;
    },&lt;br /&gt;
    setupEventListeners: function() {&lt;br /&gt;
      const questCheckboxes = safeQuerySelectorAll(SELECTORS.QUEST_CHECKBOX);&lt;br /&gt;
      Array.from(questCheckboxes).forEach(cb =&amp;gt; {&lt;br /&gt;
        cb.addEventListener(&amp;#039;change&amp;#039;, () =&amp;gt; {&lt;br /&gt;
          const section = cb.closest(&amp;#039;.location-section&amp;#039;);&lt;br /&gt;
          const items = section ? safeQuerySelectorAll(SELECTORS.QUEST_CHECKBOX, section) : [];&lt;br /&gt;
          const arr = Array.from(items);&lt;br /&gt;
          if (cb.checked) {&lt;br /&gt;
            arr.forEach(other =&amp;gt; { if (other !== cb) { other.checked = false; other.disabled = true; } });&lt;br /&gt;
          } else {&lt;br /&gt;
            const anyChecked = arr.some(x =&amp;gt; x.checked);&lt;br /&gt;
            if (!anyChecked) arr.forEach(other =&amp;gt; { other.disabled = false; });&lt;br /&gt;
          }&lt;br /&gt;
          this.saveQuestProgress();&lt;br /&gt;
          this.syncRegionToggles();&lt;br /&gt;
          this.applyDisabledStateAllRegions();&lt;br /&gt;
          this.updateProgressDisplay();&lt;br /&gt;
        });&lt;br /&gt;
      });&lt;br /&gt;
      const regionCheckboxes = safeQuerySelectorAll(SELECTORS.REGION_CHECKBOX);&lt;br /&gt;
      Array.from(regionCheckboxes).forEach(rcb =&amp;gt; {&lt;br /&gt;
        rcb.addEventListener(&amp;#039;change&amp;#039;, () =&amp;gt; { this.handleRegionToggle(rcb); });&lt;br /&gt;
      });&lt;br /&gt;
      const resetButton = safeGetElementById(&amp;#039;reset-progress&amp;#039;);&lt;br /&gt;
      if (resetButton) {&lt;br /&gt;
        resetButton.addEventListener(&amp;#039;click&amp;#039;, () =&amp;gt; { this.resetAllProgress(); });&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    handleRegionToggle: function(regionCb) {&lt;br /&gt;
      const region = regionCb ? regionCb.getAttribute(&amp;#039;data-region&amp;#039;) : &amp;#039;&amp;#039;;&lt;br /&gt;
      const checked = !!(regionCb &amp;amp;&amp;amp; regionCb.checked);&lt;br /&gt;
      if (!region) return;&lt;br /&gt;
      this.setRegionChecked(region, checked);&lt;br /&gt;
    },&lt;br /&gt;
    setRegionChecked: function(region, checked) {&lt;br /&gt;
      const items = safeQuerySelectorAll(SELECTORS.QUEST_CHECKBOX + `[data-region=&amp;quot;${region}&amp;quot;]`);&lt;br /&gt;
      const arr = Array.from(items);&lt;br /&gt;
      if (checked) {&lt;br /&gt;
        arr.forEach(cb =&amp;gt; { cb.checked = false; cb.disabled = true; });&lt;br /&gt;
        if (arr.length &amp;gt; 0) { arr[0].checked = true; arr[0].disabled = false; }&lt;br /&gt;
      } else {&lt;br /&gt;
        arr.forEach(cb =&amp;gt; { cb.checked = false; cb.disabled = false; });&lt;br /&gt;
      }&lt;br /&gt;
      this.saveQuestProgress();&lt;br /&gt;
      this.syncRegionToggles();&lt;br /&gt;
      this.applyDisabledStateAllRegions();&lt;br /&gt;
      this.updateProgressDisplay();&lt;br /&gt;
    },&lt;br /&gt;
    collectRegions: function() {&lt;br /&gt;
      const result = {};&lt;br /&gt;
      const questCheckboxes = safeQuerySelectorAll(SELECTORS.QUEST_CHECKBOX);&lt;br /&gt;
      Array.from(questCheckboxes).forEach(cb =&amp;gt; {&lt;br /&gt;
        const region = cb.getAttribute(&amp;#039;data-region&amp;#039;) || &amp;#039;global&amp;#039;;&lt;br /&gt;
        if (!result[region]) result[region] = [];&lt;br /&gt;
        result[region].push(cb);&lt;br /&gt;
      });&lt;br /&gt;
      return result;&lt;br /&gt;
    },&lt;br /&gt;
    loadQuestProgress: function() {&lt;br /&gt;
      const saved = safeGetLocalStorage(STORAGE_KEY);&lt;br /&gt;
      if (saved) {&lt;br /&gt;
        try {&lt;br /&gt;
          const data = JSON.parse(saved);&lt;br /&gt;
          Object.keys(data).forEach(id =&amp;gt; {&lt;br /&gt;
            const cb = safeQuerySelector(`[data-quest=&amp;quot;${id}&amp;quot;]`);&lt;br /&gt;
            if (cb) cb.checked = !!data[id];&lt;br /&gt;
          });&lt;br /&gt;
        } catch (_) {}&lt;br /&gt;
      }&lt;br /&gt;
      this.enforceSingleSelectionPerRegion();&lt;br /&gt;
      this.syncRegionToggles();&lt;br /&gt;
      this.applyDisabledStateAllRegions();&lt;br /&gt;
    },&lt;br /&gt;
    saveQuestProgress: function() {&lt;br /&gt;
      const questCheckboxes = safeQuerySelectorAll(SELECTORS.QUEST_CHECKBOX);&lt;br /&gt;
      const out = {};&lt;br /&gt;
      Array.from(questCheckboxes).forEach(cb =&amp;gt; {&lt;br /&gt;
        const id = cb.getAttribute(&amp;#039;data-quest&amp;#039;);&lt;br /&gt;
        if (id) out[id] = !!cb.checked;&lt;br /&gt;
      });&lt;br /&gt;
      try { safeSetLocalStorage(STORAGE_KEY, JSON.stringify(out)); } catch (_) {}&lt;br /&gt;
    },&lt;br /&gt;
    updateProgressDisplay: function() {&lt;br /&gt;
      const regions = this.collectRegions();&lt;br /&gt;
      const keys = Object.keys(regions).filter(r =&amp;gt; r !== &amp;#039;global&amp;#039;);&lt;br /&gt;
      const regionCount = keys.length;&lt;br /&gt;
      const completedRegions = keys.filter(r =&amp;gt; regions[r].some(cb =&amp;gt; cb.checked)).length;&lt;br /&gt;
      const completionPercentage = regionCount &amp;gt; 0 ? Math.round((completedRegions / regionCount) * 100) : 0;&lt;br /&gt;
      const totalProgressElement = safeGetElementById(&amp;#039;total-progress&amp;#039;);&lt;br /&gt;
      const completionPercentageElement = safeGetElementById(&amp;#039;completion-percentage&amp;#039;);&lt;br /&gt;
      const progressFillElement = safeGetElementById(&amp;#039;progress-fill&amp;#039;);&lt;br /&gt;
      if (totalProgressElement) totalProgressElement.textContent = `${completedRegions}/${regionCount}`;&lt;br /&gt;
      if (completionPercentageElement) completionPercentageElement.textContent = `${completionPercentage}%`;&lt;br /&gt;
      if (progressFillElement) progressFillElement.style.width = `${completionPercentage}%`;&lt;br /&gt;
    },&lt;br /&gt;
    resetAllProgress: function() {&lt;br /&gt;
      const questCheckboxes = safeQuerySelectorAll(SELECTORS.QUEST_CHECKBOX);&lt;br /&gt;
      Array.from(questCheckboxes).forEach(cb =&amp;gt; { cb.checked = false; cb.disabled = false; });&lt;br /&gt;
      try { localStorage.removeItem(STORAGE_KEY); } catch (_) {}&lt;br /&gt;
      this.syncRegionToggles();&lt;br /&gt;
      this.applyDisabledStateAllRegions();&lt;br /&gt;
      this.updateProgressDisplay();&lt;br /&gt;
    },&lt;br /&gt;
    syncRegionToggles: function() {&lt;br /&gt;
      const regionCheckboxes = safeQuerySelectorAll(SELECTORS.REGION_CHECKBOX);&lt;br /&gt;
      Array.from(regionCheckboxes).forEach(rc =&amp;gt; {&lt;br /&gt;
        const region = rc.getAttribute(&amp;#039;data-region&amp;#039;);&lt;br /&gt;
        if (!region) return;&lt;br /&gt;
        const items = safeQuerySelectorAll(SELECTORS.QUEST_CHECKBOX + `[data-region=&amp;quot;${region}&amp;quot;]`);&lt;br /&gt;
        const arr = Array.from(items);&lt;br /&gt;
        rc.checked = arr.some(cb =&amp;gt; cb.checked);&lt;br /&gt;
      });&lt;br /&gt;
    },&lt;br /&gt;
    enforceSingleSelectionPerRegion: function() {&lt;br /&gt;
      const sections = safeQuerySelectorAll(SELECTORS.LOCATION_SECTION);&lt;br /&gt;
      Array.from(sections).forEach(section =&amp;gt; {&lt;br /&gt;
        const items = Array.from(safeQuerySelectorAll(SELECTORS.QUEST_CHECKBOX, section));&lt;br /&gt;
        const checked = items.filter(cb =&amp;gt; cb.checked);&lt;br /&gt;
        if (checked.length &amp;gt; 1) {&lt;br /&gt;
          checked.slice(1).forEach(cb =&amp;gt; { cb.checked = false; });&lt;br /&gt;
        }&lt;br /&gt;
      });&lt;br /&gt;
      this.saveQuestProgress();&lt;br /&gt;
    },&lt;br /&gt;
    applyDisabledStateAllRegions: function() {&lt;br /&gt;
      const sections = safeQuerySelectorAll(SELECTORS.LOCATION_SECTION);&lt;br /&gt;
      Array.from(sections).forEach(section =&amp;gt; {&lt;br /&gt;
        const items = Array.from(safeQuerySelectorAll(SELECTORS.QUEST_CHECKBOX, section));&lt;br /&gt;
        const selected = items.find(cb =&amp;gt; cb.checked);&lt;br /&gt;
        if (selected) {&lt;br /&gt;
          items.forEach(cb =&amp;gt; { cb.disabled = cb !== selected; });&lt;br /&gt;
        } else {&lt;br /&gt;
          items.forEach(cb =&amp;gt; { cb.disabled = false; });&lt;br /&gt;
        }&lt;br /&gt;
      });&lt;br /&gt;
    }&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  window.QuestTracker = QuestTracker;&lt;br /&gt;
  window.initQuestTracker = QuestTracker.init.bind(QuestTracker);&lt;br /&gt;
  window.loadQuestProgress = QuestTracker.loadQuestProgress.bind(QuestTracker);&lt;br /&gt;
  window.saveQuestProgress = QuestTracker.saveQuestProgress.bind(QuestTracker);&lt;br /&gt;
  window.updateProgressDisplay = QuestTracker.updateProgressDisplay.bind(QuestTracker);&lt;br /&gt;
  window.resetAllProgress = QuestTracker.resetAllProgress.bind(QuestTracker);&lt;br /&gt;
  if (document.readyState === &amp;#039;loading&amp;#039;) {&lt;br /&gt;
    document.addEventListener(&amp;#039;DOMContentLoaded&amp;#039;, function() { QuestTracker.init(); });&lt;br /&gt;
  } else {&lt;br /&gt;
    QuestTracker.init();&lt;br /&gt;
  }&lt;br /&gt;
})();&lt;/div&gt;</summary>
		<author><name>Noorisei</name></author>
	</entry>
</feed>