Difference between revisions of "MediaWiki:SCalScript.js"

From Istaria Lexica

(Big script import)
(Replaced content with "let SCalStateCheck = setInterval(() => { 'use strict'; if (document.readyState === 'complete') { clearInterval(SCalStateCheck); let mainDivId = 'SCalMain'; //...")
Tag: Replaced
 
(6 intermediate revisions by one other user not shown)
Line 1: Line 1:
/**
+
let SCalStateCheck = setInterval(() => {
* Skeleton Key Calculator V 0.8
+
   'use strict';
* Elteria Shadowhand
+
  if (document.readyState === 'complete') {
*/
+
     clearInterval(SCalStateCheck);
$(document).ready(function() {
+
     let mainDivId = 'SCalMain'; // ID of the main content box. Can be a <div> or whatever.
   if (document.getElementById('SCalMain')) {
+
    if (document.getElementById(mainDivId)) {
    'use strict';
+
       var url = mw.util.getUrl('Product Calculator', { product: 'Skeleton Key' });
    let baseResources; // Collection of the needed base resources. To be filled.
+
       window.location.href = url;
    let refinedResources; // Collection of the needed refined resources. To be filled.
 
    let skillsOverview; // A list of all needed skills, their min/opt stats and an id
 
    let productsDetails = {}; // detailed dynamic information for each product
 
    let maxKeyCount = 5000; // Skeleton Key counter maximum value
 
    let maxSkillCount = 2000; // Skill input fields maximum value
 
    // Set to false for the script to not use the MediaWiki API (for local execution outside of wikis)
 
    // This invalidates all URLs and removes other mediawiki specific generated content, i.e. tablesorters and stylesheets
 
    let hasMW = true;
 
    let currentProductIdCount; // Dirty little helper for adding dynamic IDs to the products
 
    let cookieParameters = '; path=/; SameSite=Strict';
 
   
 
    // The dataset
 
    // Note that the skill is NOT the actual skill you need to create the product!
 
    // It is the skill you need to create it's parent product. So if you created a Spell Shard which needs Spellcraft,
 
    // the subcomponent (i.e. a Stone Brick, which would be Stoneworking) of it would be Spellcraft. The min and max
 
    // parameters behave the same.
 
    // the REAL needed skills are documented in the skillsOverview variable.
 
    let products = {
 
      'Skeleton Key': { skill: 'Tinkering', minCount: 1, maxCount: 1, minSkill: 1100, maxSkill: 1425, type: 'SCalProduct', subs:
 
        {
 
          'Enchanted Adamantium-Mithril Bar': { skill: 'Tinkering', minCount: 3, maxCount: 6, minSkill: 1100, maxSkill: 1425, type: 'SCalProduct', subs:
 
            {
 
              'Gozar\'s Blessing': { skill: 'Tinkering', minCount: 2, maxCount: 4, minSkill: 1100, maxSkill: 1425, type: 'SCalProduct', subs:
 
                {
 
                  'Meltanis\' Prayer': { skill: 'Scribing', minCount: 1, maxCount: 3, minSkill: 1100, maxSkill: 1425, type: 'SCalProduct', subs:
 
                    {
 
                      'Travertine Spell Shard': { skill: 'Spellcraft', minCount: 1, maxCount: 5, minSkill: 1100, maxSkill: 1425, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Travertine Brick': { skill: 'Stoneworking', minCount: 2, maxCount: 5, minSkill: 1000, maxSkill: 1325, type: 'SCalRefinedResource', subs:
 
                            {
 
                              'Travertine Slab': { skill: 'Stoneworking', minCount: 2, maxCount: 5, minSkill: 1000, maxSkill: 1325 }
 
                            }
 
                          }
 
                        }
 
                      }, 'Radiant Essence Orb': { skill: 'Spellcraft', minCount: 4, maxCount: 10, minSkill: 1100, maxSkill: 1425, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Radiant Essence': { skill: 'Essence Shaping', minCount: 2, maxCount: 5, minSkill: 1000, maxSkill: 1325 }
 
                        }
 
                      }
 
                    }
 
                  }, 'Primal Burst III': { skill: 'Spellcraft', minCount: 1, maxCount: 3,minSkill: 930, maxSkill: 1130, type: 'SCalProduct', subs:
 
                    {
 
                      'Marble Spell Shard': { skill: 'Spellcraft', minCount: 5, maxCount: 5, minSkill: 930, maxSkill: 1130, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Marble Brick': { skill: 'Spellcraft', minCount: 2, maxCount: 5, minSkill: 800, maxSkill: 1100, type: 'SCalRefinedResource', subs:
 
                            {
 
                              'Marble Slab': { skill: 'Stoneworking', minCount: 2, maxCount: 5, minSkill: 800, maxSkill: 1100 }
 
                            }
 
                          }
 
                        }
 
                      }, 'Shining Essence Orb': { skill: 'Spellcraft', minCount: 9, maxCount: 18, minSkill: 930, maxSkill: 1130, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Shining Essence': { skill: 'Essence Shaping', minCount: 2, maxCount: 5, minSkill: 800, maxSkill: 1100 }
 
                        }
 
                      }
 
                    }
 
                  }, 'Energy Strike V': { skill: 'Spellcraft', minCount: 1, maxCount: 3, minSkill: 850, maxSkill: 1050, type: 'SCalProduct', subs:
 
                    {
 
                      'Marble Spell Shard': { skill: 'Spellcraft', minCount: 4, maxCount: 4, minSkill: 850, maxSkill: 1050, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Marble Brick': { skill: 'Spellcraft', minCount: 2, maxCount: 5, minSkill: 800, maxSkill: 1100, type: 'SCalRefinedResource', subs:
 
                            {
 
                              'Marble Slab': { skill: 'Stoneworking', minCount: 2, maxCount: 5, minSkill: 800, maxSkill: 1100 }
 
                            }
 
                          }
 
                        }
 
                      }, 'Shining Essence Orb': { skill: 'Spellcraft', minCount: 7, maxCount: 14, minSkill: 800, maxSkill: 1100, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Shining Essence': { skill: 'Essence Shaping', minCount: 2, maxCount: 5, minSkill: 800, maxSkill: 1100 }
 
                        }
 
                      }
 
                    }
 
                  }, 'Ice Bomb V': { skill: 'Spellcraft', minCount: 1, maxCount: 3, minSkill: 920, maxSkill: 1120, type: 'SCalProduct', subs:
 
                    {
 
                      'Marble Spell Shard': { skill: 'Spellcraft', minCount: 5, maxCount: 5, minSkill: 800, maxSkill: 1100, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Marble Brick': { skill: 'Stoneworking', minCount: 2, maxCount: 5, minSkill: 800, maxSkill: 1100, type: 'SCalRefinedResource', subs:
 
                            {
 
                              'Marble Slab': { skill: 'Stoneworking', minCount: 2, maxCount: 5, minSkill: 800, maxSkill: 1100 }
 
                            }
 
                          }
 
                        }
 
                      }, 'Shining Essence Orb': { skill: 'Spellcraft', minCount: 9, maxCount: 18, minSkill: 920, maxSkill: 1120, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Shining Essence': { skill: 'Essence Shaping', minCount: 2, maxCount: 5, minSkill: 800, maxSkill: 1100 }
 
                        }
 
                      }
 
                    }
 
                  }, 'Fiery Strike V': { skill: 'Spellcraft', minCount: 1, maxCount: 3, minSkill: 980, maxSkill: 1180, type: 'SCalProduct', subs:
 
                    {
 
                      'Marble Spell Shard': { skill: 'Spellcraft', minCount: 6, maxCount: 6, minSkill: 800, maxSkill: 1100, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Marble Brick': { skill: 'Stoneworking', minCount: 2, maxCount: 5, minSkill: 800, maxSkill: 1100, type: 'SCalRefinedResource', subs:
 
                            {
 
                              'Marble Slab': { skill: 'Stoneworking', minCount: 2, maxCount: 5, minSkill: 800, maxSkill: 1100 }
 
                            }
 
                          }
 
                        }
 
                      }, 'Shining Essence Orb': { skill: 'Spellcraft', minCount: 11, maxCount: 22, minSkill: 980, maxSkill: 1180, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Shining Essence': { skill: 'Essence Shaping', minCount: 2, maxCount: 5, minSkill: 800, maxSkill: 1100 }
 
                        }
 
                      }
 
                    }
 
                  }, 'Gold Papyrus Sheet': { skill: 'Scribing', minCount: 2, maxCount: 5, minSkill: 1100, maxSkill: 1425, type: 'SCalRefinedResource', subs:
 
                    {
 
                      'Gold Papyrus Stem': { skill: 'Papermaking', minCount: 2, maxCount: 5, minSkill: 1000, maxSkill: 1325 }
 
                    }
 
                  }
 
                }
 
              }, 'Hardened Adamantium-Mithril Bar': { skill: 'Tinkering', minCount: 2, maxCount: 5, minSkill: 1100, maxSkill: 1425, type: 'SCalRefinedResource', subs:
 
                {
 
                  'Solution of Majorita': { skill: 'Tinkering', minCount: 2, maxCount: 5, minSkill: 1100, maxSkill: 1425, type: 'SCalRefinedResource', subs:
 
                    {
 
                      'Thornwood Bowl': { skill: 'Alchemy', minCount: 1, maxCount: 3, minSkill: 1100, maxSkill: 1425, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Thornwood Sap': { skill: 'Fletching', minCount: 3, maxCount: 6, minSkill: 1100, maxSkill: 1425 }
 
                          , 'Thornwood Board': { skill: 'Fletching', minCount: 5, maxCount: 10, minSkill: 1100, maxSkill: 1425, type: 'SCalRefinedResource', subs:
 
                            {
 
                              'Thornwood Log': { skill: 'Lumbering', minCount: 2, maxCount: 5, minSkill: 1000, maxSkill: 1325 }
 
                            }
 
                          }
 
                        }
 
                      }, 'Crystallized Travertine Brick': { skill: 'Alchemy', minCount: 5, maxCount: 10, minSkill: 1000, maxSkill: 1325, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Unfocused Violet Azulyte Crystal': { skill: 'Stoneworking', minCount: 1, maxCount: 5, minSkill: 1000, maxSkill: 1325 }
 
                          , 'Travertine Slab': { skill: 'Stoneworking', minCount: 1, maxCount: 5, minSkill: 1000, maxSkill: 1325 }
 
                        }
 
                      }, 'Purified Radiant Essence Orb': { skill: 'Alchemy', minCount: 1, maxCount: 4, minSkill: 1100, maxSkill: 1425, type: 'SCalRefinedResource', subs:
 
                        {
 
                          'Radiant Essence Orb': { skill: 'Alchemy', minCount: 1, maxCount: 4, minSkill: 1100, maxSkill: 1325, type: 'SCalRefinedResource', subs:
 
                            {
 
                              'Radiant Essence': { skill: 'Essence Shaping', minCount: 2, maxCount: 5, minSkill: 1000, maxSkill: 1325 }
 
                            }
 
                          }
 
                        }
 
                      }, 'Water': { skill: 'Alchemy', minCount: 15, maxCount: 30, minSkill: 1100, maxSkill: 1425 }
 
                    }
 
                  }, 'Adamantium-Mithril Bar': { skill: 'Tinkering', minCount: 1, maxCount: 1, minSkill: 1100, maxSkill: 1425, type: 'SCalRefinedResource', subs:
 
                    {
 
                      'Mithril Ore': { skill: 'Smelting', minCount: 3, maxCount: 6, minSkill: 1100, maxSkill: 1425 }
 
                      , 'Adamantium Ore': { skill: 'Smelting', minCount: 3, maxCount: 6, minSkill: 1100, maxSkill: 1425 }
 
                    }
 
                  }
 
                }
 
              }
 
            }
 
          }, 'Skeleton Key Mold': { skill: 'Tinkering', minCount: 1, maxCount: 1, minSkill: 1100, maxSkill: 1425, type: 'SCalProduct', subs:
 
            {
 
              'Skeleton Key Pattern': { skill: 'Earthencraft', minCount: 1, maxCount: 1, minSkill: 1100, maxSkill: 1425 }
 
              , 'Porcelain Clay Chunk': { skill: 'Earthencraft', minCount: 2, maxCount: 5, minSkill: 1100, maxSkill: 1425 }
 
            }
 
          }
 
        }
 
      }
 
    };
 
 
 
    // List of the skills you need to have for crafting everything, only used for the input fields
 
    skillsOverview = {
 
      'Tinkering': { min: 1100, max: 1425 },
 
      'Scribing': { min: 1100, max: 1425 },
 
      'Spellcraft': { min: 1100, max: 1425 },
 
      'Essence Shaping': { min: 1000, max: 1325 },
 
      'Papermaking': { min: 1000, max: 1325 },
 
      'Alchemy': { min: 1100, max: 1425 },
 
      'Fletching': { min: 1100, max: 1425 },
 
      'Enchanting': { min: 1100, max: 1425 },
 
      'Stoneworking': { min: 1000, max: 1325 },
 
      'Lumbering': { min: 1000, max: 1325 },
 
      'Smelting': { min: 1100, max: 1425 },
 
      'Earthencraft': { min: 1100, max: 1425 }
 
    };
 
   
 
    // dynamically generate input field IDs for skills
 
    for(var [key, values] of Object.entries(skillsOverview)) {
 
      values.id = 'SCal' + key.replace(/\s/, '') + 'Input';
 
    }
 
 
 
     // css stylesheet
 
    if(hasMW) mw.loader.load( getURL('MediaWiki:SCalScript.css') + "?action=raw&ctype=text/css", "text/css" );
 
 
 
     // Let's start adding content
 
    // top part of the gui. This is static.
 
    printHeader();
 
    printSkillInputs();
 
    printKeyCounter();
 
 
 
    // Container for the 'Export Everything' feature
 
    $('#SCalMain').append('<div id="SCalExportEverything" style="text-align: center"></div>');
 
   
 
    // Containers for the lists
 
    $('#SCalMain').append('<center><table>'
 
      + '<tr valign="top">'
 
      + '<td id="SCalResourcesrow1"></td>'
 
      + '<td id="SCalResourcesrow2"></td>'
 
      + '</tr>'
 
      + '</table></center>'
 
    );
 
 
 
    // finally create the lists.
 
    printLists();
 
 
 
    //FUNCTIONS:
 
    function printKeyCounter() {
 
      $('#SCalMain').append('<center><label for="SCalCountInput" id="SCalCountInputLbl">How many Skeleton Keys would you want to create?</label></center>');
 
      $('#SCalMain').append('<center><input type="number" id="SCalCountInput" name="SCalCountInput" value="1" size="6"></center>');
 
      $('#SCalCountInput').change(function(event) {
 
        var value = event.target.value;
 
        if(!isRealInteger(value)) value = 1;
 
        if(value <= 0) value = 1;
 
        if(value > maxKeyCount) value = maxKeyCount;
 
       
 
        event.target.value = value;
 
        setCookieByInteger(event.target.id, value);
 
        printLists();
 
      });
 
    }
 
   
 
    function printHeader() {
 
      $('#SCalMain').append('<h1 id="SCalH1">The Calculator</h1>');
 
      $('#SCalMain').append('<center>Please enter your current skills in the fields below. Defaults to optimal skills.</center>');
 
      $('#SCalMain').append('<center><input type="button" class="SCal" id="SCalMaxSkillsBtn" value="Reset to optimal skills"></center>');
 
      $('#SCalMain').append('<center><div id="SCalSkills" style="column-count:3; width:fit-content; text-align:left"></div></center>');
 
      $('#SCalMain').append('<br /><center>A <font style="color: red"><b>red</b></font> box means there is an error.</center>');
 
      $('#SCalMain').append('<center>A <font style="color: green"><b>green</b></font> box means you\'re optimal for creating the items.</center>');
 
      $('#SCalMain').append('<div id="SCalErrorText"></div>');
 
      $('#SCalMain').append('<br />');
 
      $('#SCalMain').append('<hr />');
 
 
 
      $('#SCalMaxSkillsBtn').click(function() {
 
        if(confirm('This will reset all of the skills to optimal! Are you really sure? ')) {
 
          for (var v of Object.values(skillsOverview)) {
 
            $('#' + v.id).val(v.max);
 
            $('#' + v.id).change();
 
          }
 
        }
 
      });
 
    }
 
 
 
    function generateExportLink(text, buttontext, id, mainId, mimetype, filename) {
 
       $('#' + id).remove();
 
      $('#' + mainId).append('<a id="' + id + 'A"></a>');
 
      $('#' + id + 'A').append('<input class="SCal" id="' + id + '" name="' + id + '" type="button" value="' + buttontext + '">');
 
      $('#' + id).click(function() {
 
        var url = 'data:' + mimetype + ';charset=utf-8,' + encodeURIComponent(text);
 
        $('#' + id + 'A').attr('href', url);
 
        $('#' + id + 'A').attr('download', filename);
 
      });
 
    }
 
 
 
    // Creates a simple products object, containing the needed resources
 
    function recursiveCreateProductTree(productsDetails) {
 
      var product = {};
 
      for(var [key, values] of Object.entries(productsDetails)) {
 
        product[key] = { needed: values.needed };
 
       
 
        if(values.hasOwnProperty('subs')) product[key].subs = recursiveCreateProductTree(values.subs);
 
      }
 
      return product;
 
    }
 
 
 
    function printExportEverythingLinks() {
 
      var JSONeverything = JSON.stringify({
 
        'Product Tree': recursiveCreateProductTree(productsDetails),
 
        'Base Resources': baseResources,
 
        'Refined Resources': refinedResources,
 
        'Skills': generateSkillsAsJSON()
 
      });
 
      generateExportLink(JSONeverything, 'Export everything as JSON', 'SCalExportEverythingAsJSON', 'SCalExportEverything', 'application/json', 'Skeleton Key - All.json');
 
     
 
      var TEXTeverything = '--- Tree of needed Resources ---\n\n';
 
      TEXTeverything += recursiveGenerateTreeAsTEXT(productsDetails, 0);
 
      TEXTeverything += '\n\n';
 
      TEXTeverything += '--- Needed Base Resources ---\n\n';
 
      TEXTeverything += generateResourcesAsTEXT(baseResources);
 
      TEXTeverything += '\n\n';
 
      TEXTeverything += '--- Needed Refined Resources ---\n\n';
 
      TEXTeverything += generateResourcesAsTEXT(refinedResources);
 
      TEXTeverything += '\n\n';
 
      TEXTeverything += '--- Needed Skills ---\n\n';
 
      TEXTeverything += generateSkillsAsTEXT(9,7);
 
      generateExportLink(TEXTeverything, 'Export everything as TEXT', 'SCalExportEverythingAsEXT', 'SCalExportEverything', 'text/plain', 'Skeleton Key - All.txt');
 
 
 
      var XMLeverything = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n';
 
      XMLeverything += '<ResourcesTree>\n';
 
      XMLeverything += recursiveGenerateTreeAsXML(productsDetails, 2);
 
      XMLeverything += '</ResourcesTree>\n';
 
      XMLeverything += generateResourcesAsXML(baseResources,'BaseResources', 0);
 
      XMLeverything += generateResourcesAsXML(refinedResources,'RefinedResources', 0);
 
      XMLeverything += generateSkillsAsXML(0);
 
      generateExportLink(XMLeverything, 'Export everything as XML', 'SCalExportEverythingAsXML', 'SCalExportEverything', 'application/xml', 'Skeleton Key - All.xml');
 
    }
 
   
 
    function generateSkillsAsJSON() {
 
      var json = {};
 
      for(var [key, values] of Object.entries(skillsOverview).sort()) {
 
        json[key] = {
 
          minimal: values.min,
 
          optimal: values.max
 
        };
 
      }
 
      return json;
 
    }
 
   
 
    function recursiveGenerateTreeAsTEXT(productsDetails, indent) {
 
      var text = '';
 
      for(var [key, value] of Object.entries(productsDetails)) {
 
        for(var i=0; i<indent; i++) {
 
          text += ' ';
 
        }
 
       
 
        text += '|- ';
 
        text += key + ' ';
 
        text += value.needed;
 
        text += '\n';
 
 
 
        if(value.hasOwnProperty('subs')) {
 
          text += recursiveGenerateTreeAsTEXT(value.subs, indent+2);
 
        }
 
      }
 
      return text;
 
    }
 
 
 
    function recursiveGenerateTreeAsXML(productsDetails, indent) {
 
      var xml = '';
 
      for(var [key, value] of Object.entries(productsDetails)) {
 
        for(var i=0; i<indent; i++) {
 
          xml += ' ';
 
        }
 
       
 
        xml += '<item name="' + key +'" needed="' + value.needed + '">\n';
 
 
 
        if(value.hasOwnProperty('subs')) xml += recursiveGenerateTreeAsXML(value.subs, indent+2);
 
 
 
        for(var i=0; i<indent; i++) {
 
          xml += ' ';
 
        }
 
       
 
        xml +='</item>\n';
 
      }
 
      return xml;
 
    }
 
 
 
    function determineNeededDigitlength(resources, length) {
 
      for(var value of Object.values(resources)) {
 
        if(value.toString().length > length) length = value.toString().length;
 
      }
 
      return length;
 
    }
 
 
 
    function determineNeededDigitlengthSkills(length) {
 
      for(var key of Object.keys(skillsOverview)) {
 
        if(skillsOverview[key].min.toString().length > length) length[0] = skillsOverview[key].min.toString().length;
 
        if(skillsOverview[key].max.toString().length > length) length[1] = skillsOverview[key].max.toString().length;
 
      }
 
      return length;
 
    }
 
 
 
    // calculated the effective needed resources for the given product
 
    function calculateNeededResource(minSkill, maxSkill, minCount, maxCount, skill) {
 
        var currentSkill = $('#' + skillsOverview[skill].id).val();
 
       
 
        var percent = (currentSkill - minSkill) / (maxSkill - minSkill);
 
        var needEffective = maxCount - ((maxCount - minCount) * percent);
 
        needEffective = Math.ceil(needEffective);
 
 
 
        if (needEffective < minCount) needEffective = minCount;
 
        if (needEffective > maxCount) needEffective = maxCount;
 
 
 
        return needEffective;
 
    }
 
   
 
    function recursiveCleanHaveCookies(details) {
 
      for(var values of Object.values(details)) {
 
        var id = values.id + 'Have';
 
        deleteCookie(id);
 
       
 
        if(values.hasOwnProperty('subs')) recursiveCleanHaveCookies(values.subs);
 
      }
 
    }
 
   
 
    function printTree() {
 
      $('#SCalResourcesrow1').append('<h2>Tree of needed products</h2>');
 
     
 
      generateExportLink(JSON.stringify(recursiveCreateProductTree(productsDetails)), 'Export as JSON', 'SCalExportTreeAsJSON', 'SCalResourcesrow1', 'application/json', 'Skeleton Key - Tree.json');
 
      generateExportLink(recursiveGenerateTreeAsTEXT(productsDetails, 0), 'Export as TEXT', 'SCalExportTreeAsTEXT', 'SCalResourcesrow1', 'text/plain', 'Skeleton Key - Tree.txt');
 
      var treeAsXML = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n' + recursiveGenerateTreeAsXML(productsDetails, 0);
 
      generateExportLink(treeAsXML, 'Export as XML', 'SCalExportTreeAsXML', 'SCalResourcesrow1', 'application/xml', 'Skeleton Key - Tree.xml');
 
 
 
      $('#SCalResourcesrow1').append('<input class="SCal" type="button" id="SCalResetTree" value="Reset all have-fields">');
 
      $('#SCalResetTree').click(function() {
 
        if(confirm('This will reset all have-fields to 0. Are you really sure?')) {
 
          recursiveCleanHaveCookies(productsDetails);
 
          printLists();
 
        }
 
      });
 
     
 
      $('#SCalResourcesrow1').append(recursivePrintTreeProduct(productsDetails));
 
      recursiveAddHaveChangeEvents(productsDetails);
 
    }
 
   
 
    function recursiveAddHaveChangeEvents(details) {
 
      for(var values of Object.values(details)) {
 
        $('#' + values.id + 'Have').change(function(event) {
 
          var have = event.target.value;
 
          if(!isRealInteger(have)) have = 0;
 
          if(have < 0) have = 0;
 
          setCookieByInteger(event.target.id, have);
 
          printLists();
 
        });
 
       
 
        if(values.hasOwnProperty('subs')) recursiveAddHaveChangeEvents(values.subs);
 
      }
 
    }
 
   
 
    //Calulates the needed data for the lists and puts them into objects
 
    //for further processing
 
    function recursiveCalculateResourceData(products, previousNeeded) {
 
      var details = {};
 
      for(var [key, values] of Object.entries(products)) {
 
        currentProductIdCount += 1;
 
        var id = 'SCalItem' + currentProductIdCount;
 
        var needed = calculateNeededResource(values.minSkill, values.maxSkill, values.minCount, values.maxCount, values.skill);
 
        needEffective = needed * previousNeeded;
 
        var have = getCookie(id + 'Have');
 
        if(!isRealInteger(have)) have = 0;
 
        if(have < 0) have = 0;
 
        if(have > 0) needEffective = needEffective - have;
 
        if(needEffective < 0) needEffective = 0;
 
       
 
        //Process needed resources for this product
 
        //and set the input field ID and resource type
 
        details[key] = {
 
          id: id,
 
          needed: needEffective,
 
          type: values.type
 
        };
 
       
 
        // Add to base resources
 
        // we assume that a product without subs is a base resource
 
        if(!values.hasOwnProperty('subs')) {
 
          if(baseResources.hasOwnProperty(key)) {
 
            baseResources[key] = details[key].needed + baseResources[key];
 
          } else {
 
            baseResources[key] = details[key].needed;
 
          }
 
        }
 
 
 
        // Add to refined resources
 
        if(values.type == 'SCalRefinedResource') {
 
          if(refinedResources.hasOwnProperty(key)) {
 
            refinedResources[key] = details[key].needed + refinedResources[key];
 
          } else {
 
            refinedResources[key] = details[key].needed;
 
          }
 
        }
 
       
 
        // recurse through the sub products
 
        if(values.hasOwnProperty('subs')) details[key].subs = recursiveCalculateResourceData(values.subs, details[key].needed);
 
      }
 
      return details;
 
    }
 
   
 
    function printLists() {
 
      if(validateSkillInputs()) {
 
        // reinitialize everything
 
        $('#SCalResourcesrow1').html('');
 
        $('#SCalResourcesrow2').html('');
 
        baseResources = {};
 
        refinedResources = {};
 
        currentProductIdCount = 0;
 
        var keysWanted = getCookie('SCalCountInput');
 
        if(!isRealInteger(keysWanted)) keysWanted = 1;
 
        if(keysWanted < 0) keysWanted = 1;
 
        if(keysWanted > maxKeyCount) keysWanted = maxKeyCount;
 
        $('#SCalCountInput').val(keysWanted); // set the Skeleton Key count
 
       
 
        productsDetails = recursiveCalculateResourceData(products, keysWanted);
 
 
 
        printExportEverythingLinks();
 
        printTree();
 
        printResources(baseResources, 'Overview of Base Resources', 'Base');
 
        printResources(refinedResources, 'Overview of Refined Resources', 'Refined');
 
        printNeededSkills();
 
 
 
        if(hasMW) {
 
          mw.loader.using('jquery.tablesorter', function() { // add the tablesorter to tables
 
            $('table.sortable').tablesorter({ sortList: [{ 0: 'asc' }] })
 
          });
 
        }
 
        return true;
 
       }
 
      return false;
 
    }
 
 
 
    // generates a simple object of skills and their min/max values
 
    function generateExportSkills() {
 
      var skills = {};
 
      for(var [key, value] of Object.entries(skillsOverview).sort()) {
 
        skills[key] = {
 
          'minimal skill': value.min,
 
          'optimal skill': value.max
 
        };
 
      }
 
      return skills;
 
    }
 
 
 
    function getURL(article) {
 
      if(hasMW) return mw.util.getUrl(article);
 
      return '';
 
    }
 
 
 
    function printNeededSkills() {
 
      $('#SCalResourcesrow2').append('<h2>Required skills</h2>');
 
 
 
      generateExportLink(JSON.stringify(generateExportSkills()), 'Export as JSON', 'SCalExportSkillsAsJSON', 'SCalResourcesrow2', 'application/json', 'Skeleton Key - Skills.json');
 
      generateExportLink(generateSkillsAsTEXT(determineNeededDigitlengthSkills(9,7)), 'Export as TEXT', 'SCalExportSkillsAsTEXT', 'SCalResourcesrow2', 'text/plain', 'Skeleton Key - Skills.txt');
 
      var skillsAsXML = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n' + generateSkillsAsXML(0);
 
      generateExportLink(skillsAsXML, 'Export as XML', 'SCalExportSkillsAsXML', 'SCalResourcesrow2', 'application/xml', 'Skeleton Key - Skills.xml');
 
 
 
      $('#SCalResourcesrow2').append('<table id="SCalNeededSkillsTable" class="wikitable sortable"><thead>'
 
        + '<tr>'
 
        + '<th align="left">Skill</th>'
 
        + '<th align="left">Minimal</th>'
 
        + '<th align="left">Optimal</th>'
 
        + '</tr></thead>'
 
      );
 
 
 
      var tablecontent = '<tbody>';
 
 
 
      for(var [key, value] of Object.entries(skillsOverview).sort()) {
 
        tablecontent += '<tr>'
 
          + '<td><a href="' + getURL(key) + '">' + key + '</a></td>'
 
          + '<td>' + value.min + '</td>'
 
          + '<td>' + value.max + '</td>'
 
          + '</tr>';
 
      }
 
 
 
      tablecontent += '</tbody>';
 
      tablecontent += '</table>';
 
      $('#SCalNeededSkillsTable').append(tablecontent);
 
     
 
    }
 
   
 
    function printSkillInputs() {
 
      for (var [key, value] of Object.entries(skillsOverview)) {
 
        var id = value.id;
 
        var skill = getCookie(id);
 
        if(!isRealInteger(skill)) skill = value.max; // default to max value if cookie not correctly set
 
 
 
        $('#SCalSkills').append('<input type="number" id="' + id + '" name="' + id + '" size="6" value="' + skill + '"><label for="' + id + '">&nbsp;&nbsp;' + key + '</label><br />');
 
       
 
        $('#' + id).change(function(event) {
 
            setCookieByInteger(event.target.id, event.target.value);
 
            printLists();
 
        });
 
      }
 
    }
 
 
 
    // Tests if the provided value has a valid integer and sets a cookie for it
 
    function setCookieByInteger(id, value) {
 
      if(isRealInteger(value)) setCookie(id, value, new Date(3030,1,4));
 
    }
 
 
 
    function recursivePrintTreeProduct(details) {
 
      var html = '';
 
      for(var [key, values] of Object.entries(details)) {
 
        html += '  <ul>';
 
        html += '    <li>';
 
 
 
        if(key != 'Skeleton Key') {
 
          var have = getCookie(values.id + 'Have');
 
          if(!isRealInteger(have)) have = 0;
 
          if(have < 0) have = 0;
 
          var haveClass = '';
 
          if(have > 0) haveClass = ' SCalGreenInput';
 
          html += '      <span class="' + values.type + '"><a href="' + getURL(key) + '">' + key + '</a> (' + values.needed + ') </span>'
 
            + '      <label for="' + values.id + 'Have">have:</label><input class="SCalTreeHave' + haveClass + '" id="' + values.id + 'Have" name="' + values.id + 'Have" type="number" size="8" value="' + have + '">';
 
        } else {
 
          html += '      <span class="' + values.type + '"><a href="' + getURL(key) + '">' + key + '</a> (' + values.needed + ')</span>';
 
        }
 
        if(values.hasOwnProperty('subs')) {
 
          html += recursivePrintTreeProduct(values.subs);
 
        }
 
        html += '    </li>';
 
        html += '  </ul>';
 
      }
 
      return html;
 
    }
 
 
 
    function printResources(resources, headline, type) {
 
      $('#SCalResourcesrow2').append('<h2>' + headline + '</h2>');
 
     
 
      generateExportLink(JSON.stringify(Object.fromEntries(Object.entries(resources).sort())), 'Export as JSON', 'SCalExport' + type + 'resourcesAsJSON', 'SCalResourcesrow2', 'application/json', 'Skeleton Key - ' + type + ' Resources.json');
 
      generateExportLink(generateResourcesAsTEXT(resources), 'Export as TEXT', 'SCalExport' + type + 'resourcesAsTEXT', 'SCalResourcesrow2', 'text/plain', 'Skeleton Key - ' + type + ' Resources.txt');
 
      var resourceXML = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n' + generateResourcesAsXML(resources, type, 0);
 
      generateExportLink(resourceXML, 'Export as XML', 'SCalExport' + type + 'resourcesAsXML', 'SCalResourcesrow2', 'application/xml', 'Skeleton Key - ' + type + ' Resources.xml');
 
     
 
      $('#SCalResourcesrow2').append('<table id="SCal' + type + 'ResourceTable" class="wikitable sortable"><thead>'
 
        + '<tr>'
 
        + '<th align="left">Resource</th>'
 
        + '<th align="left">Need</th>'
 
        + '</tr></thead>'
 
      );
 
 
 
      var tablecontent = '<tbody>';
 
 
 
      for (var [key, value] of Object.entries(resources).sort()) {
 
        tablecontent += '<tr>'
 
          + '<td><a href="' + getURL(key) + '">' + key + '</a></td>'
 
          + '<td>' + value + '</td>'
 
          + '</tr>';
 
      }
 
 
 
      tablecontent += '</tbody>';
 
      tablecontent += '</table>';
 
 
 
      $('#SCal' + type + 'ResourceTable').append(tablecontent);
 
    }
 
 
 
    function generateResourcesAsTEXT(resources) {
 
      var text = '';
 
      var length = determineNeededDigitlength(resources, 0);
 
      for(var [key, count] of Object.entries(resources).sort()) {
 
        var indent = '';
 
        for(var i = 0;i < length - count.toString().length; i++) {
 
          indent += ' ';
 
        }
 
       
 
        text += count + indent + '  ' + key + '\n';
 
      }
 
      return text;
 
    }
 
   
 
    function generateSkillsAsXML(indent) {
 
      indentation = '';
 
      for(var i = 0; i < indent; i++) {
 
        indentation += ' ';
 
      }
 
     
 
      var xml = indentation + '<Skills>\n';
 
      for(var [key, values] of Object.entries(skillsOverview).sort()) {
 
        xml += indentation + '  <skill minimal="' + values.min + '" optimal="' + values.max + '">' + key + '</item>\n';
 
      }
 
      xml += indentation + '</Skills>'
 
      return xml;
 
    }
 
   
 
    function generateSkillsAsTEXT(needLength, optLength) {
 
      var text = 'Minimal  Optimal  Skillname\n';
 
      text += '--------------------------------------------------------\n';
 
 
 
      for(var [key, values] of Object.entries(skillsOverview).sort()) {
 
        var needIndent = '';
 
        var optIndent = '';
 
        for(var i = 0;i < needLength - values.min.toString().length; i++) {
 
          needIndent += ' ';
 
        }
 
 
 
        for(var i = 0;i < optLength - values.max.toString().length; i++) {
 
          optIndent += ' ';
 
        }
 
       
 
        text += values.min + needIndent + values.max + optIndent + '  ' + key + '\n';
 
      }
 
      return text;
 
    }
 
 
 
    function generateResourcesAsXML(resources, tag, indent) {
 
      var indentation = '';
 
      for(var i = 0; i < indent; i++) {
 
        indentation += ' ';
 
      }
 
      var xml = indentation + '<' + tag + '>\n';
 
     
 
      for(var [key, count] of Object.entries(resources).sort()) {
 
        xml += indentation + '  <item needed="' + count + '">' + key + '</item>\n';
 
      }
 
      xml += '</' + tag + '>\n';
 
      return xml;
 
    }
 
 
 
    function isRealInteger(number) {
 
      return (!isNaN(number) && parseInt(Number(number)) == number && !isNaN(parseInt(number, 10)));
 
    }
 
   
 
    // check if the skill's input fields contain valid entries
 
    function validateSkillInputs() {
 
      var ready = true;
 
      $('#SCalErrorText').html('');
 
 
 
      for (var [skill, values] of Object.entries(skillsOverview)) {
 
        var inputObj = $('#' + values.id);
 
       
 
        inputObj.removeClass('SCalErroreousInput');
 
        inputObj.removeClass('SCalOptimalInput');
 
       
 
        number = inputObj.val();
 
 
 
        if (!isRealInteger(number)) {
 
          $('#SCalErrorText').append('<center>' + skill + ': this is not a number!</center>');
 
          inputObj.addClass('SCalErroreousInput');
 
          ready = false;
 
        } else {
 
          if (number < 1 || number > maxSkillCount) {
 
            $('#SCalErrorText').append('<center>' + skill + ': please provide a number between 1 and ' + maxSkillCount + '</center>');
 
            inputObj.addClass('SCalErroreousInput');
 
            ready = false;
 
          }
 
         
 
          if(number < values.min) {
 
            $('#SCalErrorText').append('<center>' + skill + ': Skill ' + skill + ' is too low! (have:' + number + ', need: ' + values.min + ')</center>');
 
            inputObj.addClass('SCalErroreousInput');
 
          }
 
          if(!inputObj.hasClass('SCalErroreousInput')) {
 
            if(number >= values.max) inputObj.addClass('SCalOptimalInput');
 
          }
 
        }
 
      }
 
      return ready;
 
    }
 
   
 
    function setCookie(name, value, date) {
 
      var expires = '; expires=' + date.toGMTString();
 
      var cookie = name + '=' + value + expires + cookieParameters;
 
      document.cookie = cookie;
 
    }
 
 
 
    function getCookie(name) {
 
      var nameEQ = name + '=';
 
      var ca = document.cookie.split(';');
 
      for(var i=0;i < ca.length;i++) {
 
        var c = ca[i];
 
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
 
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
 
      }
 
      return null;
 
    }
 
   
 
    function deleteCookie(name) {
 
      var expires = '; expires=Thu, 01 Jan 1970 00:00:00 UTC';
 
      var cookie = name + '=' + expires + cookieParameters;
 
      document.cookie = cookie;
 
 
     }
 
     }
 
   }
 
   }
});
+
}, 50);

Latest revision as of 16:44, 12 August 2022

let SCalStateCheck = setInterval(() => {
  'use strict';
  if (document.readyState === 'complete') {
    clearInterval(SCalStateCheck);
    let mainDivId = 'SCalMain'; // ID of the main content box. Can be a <div> or whatever.
    if (document.getElementById(mainDivId)) {
      var url = mw.util.getUrl('Product Calculator', { product: 'Skeleton Key' });
      window.location.href = url;
    }
  }
}, 50);