<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.runerealm.org/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3AGadget-wikisync-core.js</id>
	<title>MediaWiki:Gadget-wikisync-core.js - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.runerealm.org/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3AGadget-wikisync-core.js"/>
	<link rel="alternate" type="text/html" href="https://wiki.runerealm.org/index.php?title=MediaWiki:Gadget-wikisync-core.js&amp;action=history"/>
	<updated>2026-04-11T03:10:30Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.41.1</generator>
	<entry>
		<id>https://wiki.runerealm.org/index.php?title=MediaWiki:Gadget-wikisync-core.js&amp;diff=42225&amp;oldid=prev</id>
		<title>Alex at 11:06, 20 October 2024</title>
		<link rel="alternate" type="text/html" href="https://wiki.runerealm.org/index.php?title=MediaWiki:Gadget-wikisync-core.js&amp;diff=42225&amp;oldid=prev"/>
		<updated>2024-10-20T11:06:25Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;a href=&quot;https://wiki.runerealm.org/index.php?title=MediaWiki:Gadget-wikisync-core.js&amp;amp;diff=42225&amp;amp;oldid=843&quot;&gt;Show changes&lt;/a&gt;</summary>
		<author><name>Alex</name></author>
	</entry>
	<entry>
		<id>https://wiki.runerealm.org/index.php?title=MediaWiki:Gadget-wikisync-core.js&amp;diff=843&amp;oldid=prev</id>
		<title>Alex: Created page with &quot;/**  * WikiSync:  * - Utilises our WikiSync API, with data provided by users via RuneLite.  * - Adds the ability to check a user&#039;s completed quests.  *   * Slightly adapted from https://runescape.wiki/w/MediaWiki:Gadget-questchecker-core.js  *  * @author Jayden  * @author Andmcadams  * @author Jakesterwars  * @author Cook Me Plox  * @author Lezed1  * @author Haidro  */  var CLASSES = {   QC_ACTIVE: &quot;qc-active&quot;,   QC_INPUT: &quot;qc-input&quot;,   QC_ICON: &quot;rs-qc-icon&quot;, };  var END...&quot;</title>
		<link rel="alternate" type="text/html" href="https://wiki.runerealm.org/index.php?title=MediaWiki:Gadget-wikisync-core.js&amp;diff=843&amp;oldid=prev"/>
		<updated>2024-10-13T00:29:55Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;&lt;span dir=&quot;auto&quot;&gt;&lt;span class=&quot;autocomment&quot;&gt;*  * WikiSync:  * - Utilises our WikiSync API, with data provided by users via RuneLite.  * - Adds the ability to check a user&amp;#039;s completed quests.  *   * Slightly adapted from https://runescape.wiki/w/MediaWiki:Gadget-questchecker-core.js  *  * @author Jayden  * @author Andmcadams  * @author Jakesterwars  * @author Cook Me Plox  * @author Lezed1  * @author Haidro: &lt;/span&gt;  var CLASSES = {   QC_ACTIVE: &amp;quot;qc-active&amp;quot;,   QC_INPUT: &amp;quot;qc-input&amp;quot;,   QC_ICON: &amp;quot;rs-qc-icon&amp;quot;, };  var END...&amp;quot;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;/**&lt;br /&gt;
 * WikiSync:&lt;br /&gt;
 * - Utilises our WikiSync API, with data provided by users via RuneLite.&lt;br /&gt;
 * - Adds the ability to check a user&amp;#039;s completed quests.&lt;br /&gt;
 * &lt;br /&gt;
 * Slightly adapted from https://runescape.wiki/w/MediaWiki:Gadget-questchecker-core.js&lt;br /&gt;
 *&lt;br /&gt;
 * @author Jayden&lt;br /&gt;
 * @author Andmcadams&lt;br /&gt;
 * @author Jakesterwars&lt;br /&gt;
 * @author Cook Me Plox&lt;br /&gt;
 * @author Lezed1&lt;br /&gt;
 * @author Haidro&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
var CLASSES = {&lt;br /&gt;
  QC_ACTIVE: &amp;quot;qc-active&amp;quot;,&lt;br /&gt;
  QC_INPUT: &amp;quot;qc-input&amp;quot;,&lt;br /&gt;
  QC_ICON: &amp;quot;rs-qc-icon&amp;quot;,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var ENDPOINTS = {&lt;br /&gt;
  osrs: &amp;quot;https://sync.runescape.wiki/runelite/player/username/STANDARD&amp;quot;,&lt;br /&gt;
  shatteredrelics:&lt;br /&gt;
    &amp;quot;https://sync.runescape.wiki/runelite/player/username/SHATTERED_RELICS_LEAGUE&amp;quot;, //use actual url&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var questCorrections = {&lt;br /&gt;
    // Add corrections for API quest names -&amp;gt; wiki names here.&lt;br /&gt;
    // API quest name is the key and the wiki page name is the value.&lt;br /&gt;
    &amp;quot;Desert Treasure II - The Fallen Empire&amp;quot;: &amp;quot;Desert Treasure II&amp;quot;,&lt;br /&gt;
    &amp;quot;Recipe for Disaster - Another Cook&amp;#039;s Quest&amp;quot; : &amp;quot;Recipe for Disaster/Another Cook&amp;#039;s Quest&amp;quot;,&lt;br /&gt;
    &amp;quot;Recipe for Disaster - Culinaromancer&amp;quot;       : &amp;quot;Recipe for Disaster/Defeating the Culinaromancer&amp;quot;,&lt;br /&gt;
    &amp;quot;Recipe for Disaster - Mountain Dwarf&amp;quot;       : &amp;quot;Recipe for Disaster/Freeing the Mountain Dwarf&amp;quot;,&lt;br /&gt;
    &amp;quot;Recipe for Disaster - Wartface &amp;amp; Bentnoze&amp;quot;  : &amp;quot;Recipe for Disaster/Freeing the Goblin generals&amp;quot;,&lt;br /&gt;
    &amp;quot;Recipe for Disaster - Pirate Pete&amp;quot;          : &amp;quot;Recipe for Disaster/Freeing Pirate Pete&amp;quot;,        &lt;br /&gt;
    &amp;quot;Recipe for Disaster - Lumbridge Guide&amp;quot;      : &amp;quot;Recipe for Disaster/Freeing the Lumbridge Guide&amp;quot;,&lt;br /&gt;
    &amp;quot;Recipe for Disaster - Evil Dave&amp;quot;            : &amp;quot;Recipe for Disaster/Freeing Evil Dave&amp;quot;,&lt;br /&gt;
    &amp;quot;Recipe for Disaster - King Awowogei&amp;quot;        : &amp;quot;Recipe for Disaster/Freeing King Awowogei&amp;quot;,&lt;br /&gt;
    &amp;quot;Recipe for Disaster - Sir Amik Varze&amp;quot;       : &amp;quot;Recipe for Disaster/Freeing Sir Amik Varze&amp;quot;,&lt;br /&gt;
    &amp;quot;Recipe for Disaster - Skrach Uglogwee&amp;quot;      : &amp;quot;Recipe for Disaster/Freeing Skrach Uglogwee&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  icons = {&lt;br /&gt;
    yes: &amp;#039; &amp;lt;span class=&amp;quot;&amp;#039; + CLASSES.QC_ICON + &amp;#039;&amp;quot;&amp;gt;&amp;lt;img class=&amp;quot;qc-complete&amp;quot; src=&amp;quot;//oldschool.runescape.wiki/images/Yes_check.svg?00000&amp;quot; width=&amp;quot;15px&amp;quot; &amp;gt;&amp;lt;/span&amp;gt;&amp;#039;,&lt;br /&gt;
    no: &amp;#039; &amp;lt;span class=&amp;quot;&amp;#039; + CLASSES.QC_ICON + &amp;#039;&amp;quot;&amp;gt;&amp;lt;img class=&amp;quot;qc-not-started&amp;quot; src=&amp;quot;//oldschool.runescape.wiki/images/X_mark.svg?00000&amp;quot; width=&amp;quot;13px&amp;quot; &amp;gt;&amp;lt;/span&amp;gt;&amp;#039;,&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
var wikisync = {&lt;br /&gt;
  /**&lt;br /&gt;
   * Startup method&lt;br /&gt;
   */&lt;br /&gt;
  init: function () {&lt;br /&gt;
    wikisync.createFields();&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  setCheckboxText: function (text) {&lt;br /&gt;
    $(&amp;quot;.rs-wikisync-hide-completed .oo-ui-labelElement-label&amp;quot;).each(&lt;br /&gt;
      function () {&lt;br /&gt;
        $(this).text(text);&lt;br /&gt;
      }&lt;br /&gt;
    );&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  hideCompletedEntries: function () {&lt;br /&gt;
    var selected = wikisync.hideCompletedCheckbox.isSelected();&lt;br /&gt;
    if (selected) {&lt;br /&gt;
      $(&amp;quot;.wikisync-completed&amp;quot;).hide();&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  /**&lt;br /&gt;
   * Creates the input fields for the user&amp;#039;s RSN&lt;br /&gt;
   */&lt;br /&gt;
  createFields: function () {&lt;br /&gt;
    var name;&lt;br /&gt;
    if (rs.hasLocalStorage() === true) {&lt;br /&gt;
      $.removeCookie(&amp;quot;RSN&amp;quot;, { path: &amp;quot;/&amp;quot; }); // remove any existing cookies using jQuery, will return false if it doesn&amp;#039;t exist so it&amp;#039;s fine&lt;br /&gt;
      name = localStorage.getItem(&amp;quot;rsn&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
      name = wikisync.getCookie(&amp;quot;RSN&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var gamemode = &amp;quot;osrs&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    $(&amp;quot;.&amp;quot; + CLASSES.QC_INPUT).each(function () {&lt;br /&gt;
      var input1 = new OO.ui.TextInputWidget({&lt;br /&gt;
        placeholder: &amp;quot;Display name&amp;quot;,&lt;br /&gt;
        id: &amp;quot;rs-qc-rsn&amp;quot;,&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
      if (name) {&lt;br /&gt;
        // Set input to cookie/localStorage value.&lt;br /&gt;
        input1.setValue(name);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      var button1 = new OO.ui.ButtonInputWidget({&lt;br /&gt;
        label: new OO.ui.HtmlSnippet(&lt;br /&gt;
          &amp;#039;&amp;lt;img src=&amp;quot;&amp;#039; +&lt;br /&gt;
            rs.getFileURLCached(&amp;quot;RuneLite icon.png&amp;quot;) +&lt;br /&gt;
            &amp;#039;&amp;quot; width=&amp;#039; +&lt;br /&gt;
            &amp;#039;&amp;quot;20px&amp;quot; /&amp;gt;&amp;#039; +&lt;br /&gt;
            &amp;quot; Look up&amp;quot;&lt;br /&gt;
        ),&lt;br /&gt;
        title: &amp;quot;Look up player data using RuneLite&amp;quot;,&lt;br /&gt;
        flags: [&amp;quot;primary&amp;quot;, &amp;quot;progressive&amp;quot;],&lt;br /&gt;
        classes: [&amp;quot;wikisync-lookup-button&amp;quot;],&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
      var leagueOnly = $(this).hasClass(&amp;quot;league-only&amp;quot;);&lt;br /&gt;
      if (leagueOnly) {&lt;br /&gt;
        gamemode = &amp;quot;shatteredrelics&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      var button1action = function () {&lt;br /&gt;
        if (rs.hasLocalStorage() === true) {&lt;br /&gt;
          localStorage.setItem(&amp;quot;rsn&amp;quot;, input1.value); // save in localStorage&lt;br /&gt;
        } else {&lt;br /&gt;
          wikisync.setCookie(&amp;quot;RSN&amp;quot;, input1.value, 30); // set a cookie for 30 days&lt;br /&gt;
        }&lt;br /&gt;
        wikisync.loadData(input1.value, gamemode);&lt;br /&gt;
      };&lt;br /&gt;
      button1.on(&amp;quot;click&amp;quot;, button1action);&lt;br /&gt;
      input1.on(&amp;quot;enter&amp;quot;, button1action);&lt;br /&gt;
&lt;br /&gt;
      var helpModalBtn = new OO.ui.ButtonWidget({&lt;br /&gt;
        label: &amp;quot;Learn how to enable WikiSync&amp;quot;,&lt;br /&gt;
        href: &amp;quot;/w/RuneScape:WikiSync&amp;quot;,&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
      var hideCompleted = false;&lt;br /&gt;
      if (rs.hasLocalStorage()) {&lt;br /&gt;
        if (localStorage.getItem(&amp;quot;wikisync-hide-completed&amp;quot;) === &amp;quot;true&amp;quot;) {&lt;br /&gt;
          hideCompleted = true;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      wikisync.hideCompletedCheckbox = new OO.ui.CheckboxInputWidget({&lt;br /&gt;
        selected: hideCompleted,&lt;br /&gt;
      });&lt;br /&gt;
      wikisync.hideCompletedCheckbox.on(&amp;quot;change&amp;quot;, function () {&lt;br /&gt;
        var selected = wikisync.hideCompletedCheckbox.isSelected();&lt;br /&gt;
        if (rs.hasLocalStorage() === true) {&lt;br /&gt;
          localStorage.setItem(&amp;quot;wikisync-hide-completed&amp;quot;, selected); // save in localStorage&lt;br /&gt;
        }&lt;br /&gt;
        $(&amp;quot;.wikisync-completed&amp;quot;).toggle(!selected);&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
      var fieldset = new OO.ui.FieldsetLayout({&lt;br /&gt;
        id: &amp;quot;rs-qc-form&amp;quot;,&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
      var fieldSetItems = [&lt;br /&gt;
        new OO.ui.FieldLayout(input1, { label: &amp;quot;Username:&amp;quot;, align: &amp;quot;inline&amp;quot; }),&lt;br /&gt;
      ];&lt;br /&gt;
&lt;br /&gt;
      fieldSetItems.push(button1);&lt;br /&gt;
      fieldset.addItems([&lt;br /&gt;
        new OO.ui.HorizontalLayout({&lt;br /&gt;
          items: fieldSetItems,&lt;br /&gt;
        }),&lt;br /&gt;
        new OO.ui.FieldLayout(helpModalBtn, {&lt;br /&gt;
          label:&lt;br /&gt;
            &amp;quot;No data found. To use this, enable the WikiSync plugin in RuneLite.&amp;quot;,&lt;br /&gt;
          align: &amp;quot;inline&amp;quot;,&lt;br /&gt;
          classes: [&amp;quot;rs-wikisync-help&amp;quot;],&lt;br /&gt;
        }),&lt;br /&gt;
        new OO.ui.LabelWidget({&lt;br /&gt;
          label:&lt;br /&gt;
            &amp;quot;Missing some data from RuneLite for this page. Please log in to the game to re-sync.&amp;quot;,&lt;br /&gt;
          classes: [&amp;quot;rs-wikisync-missingdata&amp;quot;],&lt;br /&gt;
        }),&lt;br /&gt;
        new OO.ui.FieldLayout(wikisync.hideCompletedCheckbox, {&lt;br /&gt;
          label: &amp;quot;Hide completed&amp;quot;,&lt;br /&gt;
          align: &amp;quot;inline&amp;quot;,&lt;br /&gt;
          classes: [&amp;quot;rs-wikisync-hide-completed&amp;quot;],&lt;br /&gt;
        }),&lt;br /&gt;
      ]);&lt;br /&gt;
&lt;br /&gt;
      if ($(this).hasClass(&amp;quot;lighttable&amp;quot;)) {&lt;br /&gt;
        // If it&amp;#039;s a lighttable, insert the fieldset before the table&lt;br /&gt;
        fieldset.$element.insertBefore(this);&lt;br /&gt;
      } else {&lt;br /&gt;
        // If not, insert it inside the element that has the class&lt;br /&gt;
        $(this).prepend(fieldset.$element);&lt;br /&gt;
      }&lt;br /&gt;
      // Hide all of the help elements to start with&lt;br /&gt;
      $(&amp;quot;.rs-wikisync-help&amp;quot;).hide();&lt;br /&gt;
      $(&amp;quot;.rs-wikisync-missingdata&amp;quot;).hide();&lt;br /&gt;
      if ($(&amp;quot;.srl-tasks, .music-tracks&amp;quot;).length === 0) {&lt;br /&gt;
        // only show checkbox if it&amp;#039;s a table with hide-able tasks&lt;br /&gt;
        $(&amp;quot;.rs-wikisync-hide-completed&amp;quot;).hide();&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (name) {&lt;br /&gt;
      // If there is a saved name, load the data for it.&lt;br /&gt;
      wikisync.loadData(name, gamemode);&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  /**&lt;br /&gt;
   * Updates the status text&lt;br /&gt;
   */&lt;br /&gt;
  updateStatus: function (text) {&lt;br /&gt;
    mw.notify(text, { tag: &amp;quot;wikisync&amp;quot; });&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  /**&lt;br /&gt;
   * Sets a cookie&lt;br /&gt;
   */&lt;br /&gt;
  setCookie: function (name, value, days) {&lt;br /&gt;
    var expires = &amp;quot;&amp;quot;;&lt;br /&gt;
    if (days) {&lt;br /&gt;
      var date = new Date();&lt;br /&gt;
      date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);&lt;br /&gt;
      expires = &amp;quot;; expires=&amp;quot; + date.toUTCString();&lt;br /&gt;
    }&lt;br /&gt;
    document.cookie = name + &amp;quot;=&amp;quot; + (value || &amp;quot;&amp;quot;) + expires + &amp;quot;; path=/&amp;quot;;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  /**&lt;br /&gt;
   * Returns the value of a cookie, or null if it doesn&amp;#039;t exist&lt;br /&gt;
   */&lt;br /&gt;
  getCookie: function (name) {&lt;br /&gt;
    var cookie = new RegExp(&lt;br /&gt;
        &amp;quot;^(?:.*;)?\\s*&amp;quot; + name + &amp;quot;\\s*=\\s*([^;]+)(?:.*)?$&amp;quot;&lt;br /&gt;
      ),&lt;br /&gt;
      match = document.cookie.match(cookie);&lt;br /&gt;
&lt;br /&gt;
    if (match !== null) {&lt;br /&gt;
      return match[1];&lt;br /&gt;
    } else {&lt;br /&gt;
      return null;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  /**&lt;br /&gt;
   * Load data&lt;br /&gt;
   */&lt;br /&gt;
  loadData: function (rsn, gamemode) {&lt;br /&gt;
    if (!rsn) {&lt;br /&gt;
      wikisync.updateStatus(&amp;quot;Invalid RSN&amp;quot;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var endpoint = ENDPOINTS[gamemode || &amp;quot;osrs&amp;quot;] || ENDPOINTS.osrs;&lt;br /&gt;
    // Hide help text if it is showing.&lt;br /&gt;
    $(&amp;quot;.rs-wikisync-help&amp;quot;).hide();&lt;br /&gt;
    $(&amp;quot;.rs-wikisync-missingdata&amp;quot;).hide();&lt;br /&gt;
    $(&amp;quot;.wikisync-success&amp;quot;).remove();&lt;br /&gt;
&lt;br /&gt;
    $.ajax({&lt;br /&gt;
      // Get the quest data&lt;br /&gt;
      type: &amp;quot;GET&amp;quot;,&lt;br /&gt;
      url: endpoint.replace(&amp;quot;username&amp;quot;, rsn),&lt;br /&gt;
      dataType: &amp;quot;json&amp;quot;,&lt;br /&gt;
      success: function (msg) {&lt;br /&gt;
        var userQuests = {};&lt;br /&gt;
        Object.entries(msg.quests).forEach(function (q) {&lt;br /&gt;
          var k = q[0],&lt;br /&gt;
            v = q[1];&lt;br /&gt;
          // Correct quest names to wiki page names&lt;br /&gt;
          if (k in questCorrections) {&lt;br /&gt;
            var correctName = questCorrections[k];&lt;br /&gt;
            userQuests[correctName] = v;&lt;br /&gt;
          } else {&lt;br /&gt;
            userQuests[k] = v;&lt;br /&gt;
          }&lt;br /&gt;
        });&lt;br /&gt;
        var userSkills = {};&lt;br /&gt;
        Object.entries(msg.levels).forEach(function (q) {&lt;br /&gt;
          var k = q[0],&lt;br /&gt;
            v = q[1];&lt;br /&gt;
          userSkills[k] = v;&lt;br /&gt;
        });&lt;br /&gt;
        $(&amp;quot;.&amp;quot; + CLASSES.QC_ICON).remove();&lt;br /&gt;
        $(&amp;quot;.wikisync-completed&amp;quot;).show();&lt;br /&gt;
        $(&amp;quot;.wikisync-completed&amp;quot;).removeClass(&amp;quot;wikisync-completed&amp;quot;);&lt;br /&gt;
        $(&amp;quot;&amp;lt;img&amp;gt;&amp;quot;)&lt;br /&gt;
          .attr(&lt;br /&gt;
            &amp;quot;src&amp;quot;,&lt;br /&gt;
            &amp;quot;//oldschool.runescape.wiki/images/f/fb/Yes_check.svg?00000&amp;quot;&lt;br /&gt;
          )&lt;br /&gt;
          .addClass(&amp;quot;wikisync-success&amp;quot;)&lt;br /&gt;
          .css(&amp;quot;width&amp;quot;, &amp;quot;20px&amp;quot;)&lt;br /&gt;
          .css(&amp;quot;height&amp;quot;, &amp;quot;20px&amp;quot;)&lt;br /&gt;
          .css(&amp;quot;position&amp;quot;, &amp;quot;relative&amp;quot;)&lt;br /&gt;
          .insertAfter(&amp;quot;.wikisync-lookup-button&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        var hasAllData = [&lt;br /&gt;
          wikisync.addQuestIcons(userQuests),&lt;br /&gt;
          wikisync.addQuestTable(&lt;br /&gt;
            userQuests,&lt;br /&gt;
            userSkills,&lt;br /&gt;
            msg.achievement_diaries&lt;br /&gt;
          ),&lt;br /&gt;
          wikisync.addDiaryTable(msg.achievement_diaries),&lt;br /&gt;
          wikisync.addSkillTable(userSkills),&lt;br /&gt;
          wikisync.addSkillIcons(userSkills),&lt;br /&gt;
          wikisync.addMusicTracks(msg.music_tracks),&lt;br /&gt;
          wikisync.addCombatAchievementTasks(msg.combat_achievements),&lt;br /&gt;
        ].every(function (result) {&lt;br /&gt;
          return result;&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        if (!hasAllData) {&lt;br /&gt;
          $(&amp;quot;.rs-wikisync-missingdata&amp;quot;).show();&lt;br /&gt;
        }&lt;br /&gt;
      },&lt;br /&gt;
      error: function (req) {&lt;br /&gt;
        $(&amp;quot;.&amp;quot; + CLASSES.QC_ICON).remove();&lt;br /&gt;
        wikisync.setCheckboxText(&amp;quot;Hide completed&amp;quot;);&lt;br /&gt;
        if (&lt;br /&gt;
          req.responseJSON &amp;amp;&amp;amp;&lt;br /&gt;
          req.responseJSON.code &amp;amp;&amp;amp;&lt;br /&gt;
          req.responseJSON.code === &amp;quot;NO_USER_DATA&amp;quot;&lt;br /&gt;
        ) {&lt;br /&gt;
          $(&amp;quot;.rs-wikisync-help&amp;quot;).show();&lt;br /&gt;
        } else {&lt;br /&gt;
          wikisync.updateStatus(&amp;quot;There was a problem loading data for &amp;quot; + rsn);&lt;br /&gt;
        }&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  /**&lt;br /&gt;
   * Clicks the Combat Achievement rows&lt;br /&gt;
   */&lt;br /&gt;
  addCombatAchievementTasks: function (combatAchievements) {&lt;br /&gt;
    var combatAchievementTable = $(&amp;#039;table.&amp;#039; + CLASSES.QC_ACTIVE + &amp;#039;.ca-tasks&amp;#039;);&lt;br /&gt;
    if (combatAchievementTable.length === 0) {&lt;br /&gt;
      // Page doesn&amp;#039;t have Combat Achievement tasks on it&lt;br /&gt;
      return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (combatAchievementTable === null) {&lt;br /&gt;
      return false;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var seen = {};&lt;br /&gt;
    combatAchievements.forEach(function (taskId) {&lt;br /&gt;
      seen[taskId] = true;&lt;br /&gt;
    });&lt;br /&gt;
    combatAchievementTable.each(function () {&lt;br /&gt;
      $(this)&lt;br /&gt;
        .find(&amp;quot;tr[data-ca-task-id]&amp;quot;)&lt;br /&gt;
        .each(function () {&lt;br /&gt;
          var task_id = $(this).data(&amp;quot;ca-task-id&amp;quot;);&lt;br /&gt;
          if (!!seen[task_id] !== $(this).hasClass(&amp;quot;highlight-on&amp;quot;)) {&lt;br /&gt;
            $(this).click();&lt;br /&gt;
          }&lt;br /&gt;
          if (seen[task_id]) {&lt;br /&gt;
            $(this).addClass(&amp;quot;wikisync-completed&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        });&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  /**&lt;br /&gt;
   * Clicks the music track rows&lt;br /&gt;
   */&lt;br /&gt;
  addMusicTracks: function (musicTracks) {&lt;br /&gt;
    var musicTable = $(&amp;quot;table.&amp;quot; + CLASSES.QC_ACTIVE + &amp;quot;.music-tracks&amp;quot;);&lt;br /&gt;
    if (musicTable.length === 0) {&lt;br /&gt;
      // Not a music track page&lt;br /&gt;
      return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (musicTracks === null) {&lt;br /&gt;
      // Missing data&lt;br /&gt;
      return false;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var total = 0;&lt;br /&gt;
    var completed = 0;&lt;br /&gt;
    musicTable.each(function () {&lt;br /&gt;
      $(this)&lt;br /&gt;
        .find(&amp;quot;tr[data-music-track-name]&amp;quot;)&lt;br /&gt;
        .each(function () {&lt;br /&gt;
          var music_track_name = $(this).data(&amp;quot;music-track-name&amp;quot;);&lt;br /&gt;
          if (!!musicTracks[music_track_name] !== $(this).hasClass(&amp;quot;highlight-on&amp;quot;)) {&lt;br /&gt;
            $(this).click();&lt;br /&gt;
          }&lt;br /&gt;
          if (musicTracks[music_track_name]) {&lt;br /&gt;
            $(this).addClass(&amp;quot;wikisync-completed&amp;quot;);&lt;br /&gt;
            completed++;&lt;br /&gt;
          }&lt;br /&gt;
          total++;&lt;br /&gt;
        });&lt;br /&gt;
    });&lt;br /&gt;
    wikisync.setCheckboxText(&lt;br /&gt;
      &amp;quot;Hide unlocked tracks (&amp;quot; + completed + &amp;quot;/&amp;quot; + total + &amp;quot; unlocked)&amp;quot;&lt;br /&gt;
    );&lt;br /&gt;
    wikisync.hideCompletedEntries();&lt;br /&gt;
    return true;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  /**&lt;br /&gt;
   * Append a checkmark/X icon to `element`.&lt;br /&gt;
   */&lt;br /&gt;
  append_icon: function (element, completed) {&lt;br /&gt;
    if (completed) {&lt;br /&gt;
      $(element).append(icons.yes);&lt;br /&gt;
    } else {&lt;br /&gt;
      $(element).append(icons.no);&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  /**&lt;br /&gt;
   * Clicks the rows in a table of question and diary tiers. Also appends icons to rows dedicated to skill training&lt;br /&gt;
   */&lt;br /&gt;
  addQuestTable: function (quests, skills, achievementDiaries) {&lt;br /&gt;
    function splitOnLastOccurence(str, splitOn) {&lt;br /&gt;
      var index = str.lastIndexOf(splitOn);&lt;br /&gt;
      return { before: str.slice(0, index), after: str.slice(index + 1) };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Quest and diary completion&lt;br /&gt;
    $(&amp;quot;table.&amp;quot; + CLASSES.QC_ACTIVE + &amp;quot;.oqg-table tr[data-rowid]&amp;quot;).each(function () {&lt;br /&gt;
      var rowID = $(this).data(&amp;quot;rowid&amp;quot;);&lt;br /&gt;
      var isAchievementDiary = rowID.includes(&amp;quot;Diary#&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      if (isAchievementDiary) {&lt;br /&gt;
        // Achievement diary rowIDs are formatted as &amp;quot;$NAME Diary#$TIER&amp;quot;, where &amp;quot;$NAME&amp;quot; may contain spaces.&lt;br /&gt;
&lt;br /&gt;
        var diaryName = splitOnLastOccurence(rowID, &amp;quot; &amp;quot;).before;&lt;br /&gt;
        var diaryTier = splitOnLastOccurence(rowID, &amp;quot;#&amp;quot;).after;&lt;br /&gt;
        if (&lt;br /&gt;
          diaryName in achievementDiaries &amp;amp;&amp;amp;&lt;br /&gt;
          diaryTier in achievementDiaries[diaryName] &amp;amp;&amp;amp;&lt;br /&gt;
          &amp;quot;complete&amp;quot; in achievementDiaries[diaryName][diaryTier]&lt;br /&gt;
        ) {&lt;br /&gt;
          var diaryCompleted =&lt;br /&gt;
            achievementDiaries[diaryName][diaryTier].complete;&lt;br /&gt;
          if (diaryCompleted !== null) {&lt;br /&gt;
            if (diaryCompleted !== $(this).hasClass(&amp;quot;highlight-on&amp;quot;)) {&lt;br /&gt;
              $(this).click();&lt;br /&gt;
            }&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      } else {&lt;br /&gt;
        var questName = rowID;&lt;br /&gt;
        if (questName in quests) {&lt;br /&gt;
          var questCompleted = quests[questName] == 2;&lt;br /&gt;
          if (questCompleted !== $(this).hasClass(&amp;quot;highlight-on&amp;quot;)) {&lt;br /&gt;
            $(this).click();&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Skill training complete&lt;br /&gt;
    $(&lt;br /&gt;
      &amp;quot;table.&amp;quot; + CLASSES.QC_ACTIVE + &amp;quot;.oqg-table tr[data-skill][data-skill-level]&amp;quot;&lt;br /&gt;
    ).each(function () {&lt;br /&gt;
      var skillName = $(this).data(&amp;quot;skill&amp;quot;);&lt;br /&gt;
      skillName = skillName.charAt(0).toUpperCase() + skillName.slice(1);&lt;br /&gt;
      var skillLevel = $(this).data(&amp;quot;skill-level&amp;quot;);&lt;br /&gt;
      var row = $(this).find(&amp;quot;th&amp;quot;);&lt;br /&gt;
      wikisync.append_icon(row, skills[skillName] &amp;gt;= skillLevel);&lt;br /&gt;
    });&lt;br /&gt;
    return true;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  // Clicks cells/rows in a table based on skill levels.&lt;br /&gt;
  addSkillTable: function (skills) {&lt;br /&gt;
    $(&lt;br /&gt;
      &amp;quot;table.&amp;quot; + CLASSES.QC_ACTIVE + &amp;quot;.skill-table [data-skill][data-skill-level]&amp;quot;&lt;br /&gt;
    ).each(function () {&lt;br /&gt;
      var skillName = $(this).data(&amp;quot;skill&amp;quot;);&lt;br /&gt;
      var skillLevel = $(this).data(&amp;quot;skill-level&amp;quot;);&lt;br /&gt;
      var skillCompleted = skills[skillName] &amp;gt;= skillLevel;&lt;br /&gt;
      if (skillCompleted !== $(this).hasClass(&amp;quot;highlight-on&amp;quot;)) {&lt;br /&gt;
        $(this).click();&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
    return true;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  // Clicks rows in a table based on achievement diary task completion&lt;br /&gt;
  addDiaryTable: function (achievementDiaries) {&lt;br /&gt;
    var hasAllData = true;&lt;br /&gt;
    $(&lt;br /&gt;
      &amp;quot;table.&amp;quot; + CLASSES.QC_ACTIVE + &amp;quot;.diary-table[data-diary-name][data-diary-tier]&amp;quot;&lt;br /&gt;
    ).each(function () {&lt;br /&gt;
      var task_index = -1;&lt;br /&gt;
      var diaryName = $(this).data(&amp;quot;diary-name&amp;quot;);&lt;br /&gt;
      var diaryTier = $(this).data(&amp;quot;diary-tier&amp;quot;);&lt;br /&gt;
      $(this)&lt;br /&gt;
        .find(&amp;quot;tr&amp;quot;)&lt;br /&gt;
        .each(function () {&lt;br /&gt;
          if (task_index &amp;lt; 0) {&lt;br /&gt;
            task_index += 1;&lt;br /&gt;
            return;&lt;br /&gt;
          }&lt;br /&gt;
          if (&lt;br /&gt;
            diaryName in achievementDiaries &amp;amp;&amp;amp;&lt;br /&gt;
            diaryTier in achievementDiaries[diaryName] &amp;amp;&amp;amp;&lt;br /&gt;
            achievementDiaries[diaryName][diaryTier].tasks.length &amp;gt; task_index&lt;br /&gt;
          ) {&lt;br /&gt;
            var task_completed =&lt;br /&gt;
              achievementDiaries[diaryName][diaryTier].tasks[task_index];&lt;br /&gt;
&lt;br /&gt;
            if (task_completed !== null) {&lt;br /&gt;
              if (task_completed !== $(this).hasClass(&amp;quot;highlight-on&amp;quot;)) {&lt;br /&gt;
                $(this).click();&lt;br /&gt;
              }&lt;br /&gt;
            } else {&lt;br /&gt;
              hasAllData = false;&lt;br /&gt;
            }&lt;br /&gt;
          }&lt;br /&gt;
          task_index += 1;&lt;br /&gt;
        });&lt;br /&gt;
    });&lt;br /&gt;
    return hasAllData;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  /**&lt;br /&gt;
   * Adds the icons next to respective quests&lt;br /&gt;
   */&lt;br /&gt;
  addQuestIcons: function (quests) {&lt;br /&gt;
    $(&amp;quot;.&amp;quot; + CLASSES.QC_ACTIVE + &amp;quot; a&amp;quot;).each(function () {&lt;br /&gt;
      if (&lt;br /&gt;
        $(this).html().toLowerCase() != &amp;quot;expand&amp;quot; ||&lt;br /&gt;
        $(this).html().toLowerCase() != &amp;quot;collapse&amp;quot;&lt;br /&gt;
      ) {&lt;br /&gt;
        var questTitle = $(this).text().trim();&lt;br /&gt;
        if (questTitle in quests) {&lt;br /&gt;
          wikisync.append_icon(this, quests[questTitle] === 2);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
    return true;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  /**&lt;br /&gt;
   * Adds the icons next to respective skills&lt;br /&gt;
   */&lt;br /&gt;
  addSkillIcons: function (userLevels) {&lt;br /&gt;
    $(&amp;quot;.&amp;quot; + CLASSES.QC_ACTIVE + &amp;quot; .scp&amp;quot;).each(function () {&lt;br /&gt;
      var level = $(this).data(&amp;quot;level&amp;quot;);&lt;br /&gt;
      var skill = $(this).data(&amp;quot;skill&amp;quot;);&lt;br /&gt;
      if (typeof level !== &amp;quot;number&amp;quot; || userLevels[skill] === undefined) {&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      wikisync.append_icon(this, userLevels[skill] &amp;gt;= level);&lt;br /&gt;
    });&lt;br /&gt;
    return true;&lt;br /&gt;
  },&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
$(wikisync.init);&lt;/div&gt;</summary>
		<author><name>Alex</name></author>
	</entry>
</feed>