|
|
(3 intermediate revisions by one other user not shown) |
Line 1: |
Line 1: |
− | /**
| + | let SCalStateCheck = setInterval(() => { |
− | * Skeleton Key Calculator
| |
− | * Elteria Shadowhand
| |
− | */
| |
− | $(document).ready(function() {
| |
| 'use strict'; | | 'use strict'; |
− | if (document.getElementById('SCalMain')) { | + | if (document.readyState === 'complete') { |
− | let version = '1.2'; // the script version
| + | clearInterval(SCalStateCheck); |
− | let baseResources; // Collection of the needed base resources. To be filled.
| + | let mainDivId = 'SCalMain'; // ID of the main content box. Can be a <div> or whatever. |
− | let refinedResources; // Collection of the needed refined resources. To be filled.
| + | if (document.getElementById(mainDivId)) { |
− | let productsDetails = {}; // detailed dynamic information for each product
| + | var url = mw.util.getUrl('Product Calculator', { product: 'Skeleton Key' }); |
− | let currentProductIdCount; // Dirty little helper for adding dynamic IDs to the products
| + | window.location.href = url; |
− | | |
− | 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 cookieParameters = '; path=/; SameSite=Strict';
| |
− | | |
− | // The dataset of resources
| |
− | // Note that the skill is NOT the actual skill you need to create the product!
| |
− | // It's the skill you would need to create its parent product. E.g. if you created a Spell Shard which originally
| |
− | // 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 }
| |
− | }
| |
− | }
| |
− | }
| |
− | }
| |
− | };
| |
− | | |
− | // A list of the skills and their limits you need to be able to craft everything
| |
− | let 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 }
| |
− | };
| |
− | | |
− | ///////////////////////////////////////////////////////////////////
| |
− | //FUNCTIONS: | |
− | var printKeyCounter = 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);
| |
− | updateLists();
| |
− | });
| |
− | }; | |
− |
| |
− | var printHeader = function printHeader() {
| |
− | var setCookie = function(event) {
| |
− | setCookieByInteger(event.target.id, event.target.value);
| |
− | updateLists();
| |
− | };
| |
− | $('#SCalMain').append('<h1 id="SCalH1">The Calculator</h1>');
| |
− | $('#SCalMain').append('<center style="font-size: 0.8em; margin-bottom:30px">(v ' + version + ')</center>');
| |
− | $('#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>');
| |
− | $('#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();
| |
− | }
| |
− | }
| |
− | });
| |
− | | |
− | $('#SCalMain').append('<center><div id="SCalSkills" style="column-count:3; width:fit-content; text-align:left"></div></center>');
| |
− | for (var [skillName, values] of Object.entries(skillsOverview)) {
| |
− | var id = values.id;
| |
− | var skillLevel = getCookie(id);
| |
− |
| |
− | if(!isRealInteger(skillLevel)) skillLevel = values.max; // default to optimal value if cookie not correctly set
| |
− |
| |
− | $('#SCalSkills').append('<input type="number" id="' + id + '" name="' + id + '" size="6" value="' + skillLevel + '"><label for="' + id + '"> ' + skillName + '</label><br />');
| |
− |
| |
− | $('#' + id).change(setCookie);
| |
− | $('#' + id).change();
| |
− | }
| |
− |
| |
− | $('#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 />');
| |
− | };
| |
− | | |
− | var printExportLink = function printExportLink(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
| |
− | var recursiveCreateProductTree = function recursiveCreateProductTree(products) {
| |
− | var treeProducts = {};
| |
− | for(var [product, values] of Object.entries(products)) {
| |
− | treeProducts[product] = { needed: values.needed };
| |
− |
| |
− | if(values.hasOwnProperty('subs')) treeProducts[product].subs = recursiveCreateProductTree(values.subs);
| |
− | }
| |
− | return treeProducts;
| |
− | };
| |
− | | |
− | var updateExportEverythingLinks = function updateExportEverythingLinks() {
| |
− | var JSONeverything = JSON.stringify({
| |
− | 'Product Tree': recursiveCreateProductTree(productsDetails),
| |
− | 'Base Resources': baseResources,
| |
− | 'Refined Resources': refinedResources,
| |
− | 'Skills': generateSkillsAsJSON()
| |
− | });
| |
− | printExportLink(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();
| |
− | printExportLink(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);
| |
− | printExportLink(XMLeverything, 'Export everything as XML', 'SCalExportEverythingAsXML', 'SCalExportEverything', 'application/xml', 'Skeleton Key - All.xml');
| |
− | };
| |
− | | |
− | var generateSkillsAsJSON = function generateSkillsAsJSON() {
| |
− | var json = {};
| |
− | for(var [skill, values] of Object.entries(skillsOverview).sort()) {
| |
− | json[skill] = {
| |
− | minimal: values.min,
| |
− | optimal: values.max
| |
− | };
| |
− | }
| |
− | return json;
| |
− | };
| |
− |
| |
− | var recursiveGenerateTreeAsTEXT = function recursiveGenerateTreeAsTEXT(productsDetails, indent) {
| |
− | var text = '';
| |
− | for(var [product, values] of Object.entries(productsDetails)) {
| |
− | for(var i=0; i<indent; i++) {
| |
− | text += ' ';
| |
− | }
| |
− |
| |
− | text += '|- ';
| |
− | text += product + ' ';
| |
− | text += values.needed;
| |
− | text += '\n';
| |
− | | |
− | if(values.hasOwnProperty('subs')) {
| |
− | text += recursiveGenerateTreeAsTEXT(values.subs, indent + 3);
| |
− | }
| |
− | }
| |
− | return text;
| |
− | };
| |
− | | |
− | var recursiveGenerateTreeAsXML = function recursiveGenerateTreeAsXML(productsDetails, indent) {
| |
− | var xml = '';
| |
− | for(var [product, value] of Object.entries(productsDetails)) {
| |
− | for(var i=0; i < indent; i++) {
| |
− | xml += ' ';
| |
− | }
| |
− |
| |
− | xml += '<item name="' + product +'" needed="' + value.needed + '">\n';
| |
− | | |
− | if(value.hasOwnProperty('subs')) xml += recursiveGenerateTreeAsXML(value.subs, indent + 2);
| |
− | | |
− | for(var j=0; j < indent; j++) {
| |
− | xml += ' ';
| |
− | }
| |
− |
| |
− | xml +='</item>\n';
| |
− | }
| |
− | return xml;
| |
− | };
| |
− | | |
− | var determineNeededDigitlengthResources = function determineNeededDigitlengthResources(resources) {
| |
− | var length = 0;
| |
− | for(var count of Object.values(resources)) {
| |
− | if(count.toString().length > length) length = count.toString().length;
| |
− | }
| |
− | return length;
| |
− | };
| |
− | | |
− | var determineNeededDigitlengthSkills = function determineNeededDigitlengthSkills() {
| |
− | var length = [9, 10];
| |
− | for(var skill of Object.keys(skillsOverview)) {
| |
− | if(skillsOverview[skill].min.toString().length > length[0]) length[0] = skillsOverview[skill].min.toString().length;
| |
− | if(skillsOverview[skill].max.toString().length > length[1]) length[1] = skillsOverview[skill].max.toString().length;
| |
− | }
| |
− | return length;
| |
− | };
| |
− | | |
− | // calculates the effective resources needed
| |
− | var calculateNeededResource = 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;
| |
− | };
| |
− |
| |
− | var recursiveCleanHaveCookies = function recursiveCleanHaveCookies(details) {
| |
− | for(var values of Object.values(details)) {
| |
− | var id = values.id + 'Have';
| |
− | deleteCookie(id);
| |
− |
| |
− | if(values.hasOwnProperty('subs')) recursiveCleanHaveCookies(values.subs);
| |
− | }
| |
− | };
| |
− |
| |
− | var updateTree = function updateTree() {
| |
− | printExportLink(JSON.stringify(recursiveCreateProductTree(productsDetails)), 'Export as JSON', 'SCalExportTreeAsJSON', 'SCalResourcesExportLinks', 'application/json', 'Skeleton Key - Tree.json');
| |
− | printExportLink(recursiveGenerateTreeAsTEXT(productsDetails, 0), 'Export as TEXT', 'SCalExportTreeAsTEXT', 'SCalResourcesExportLinks', 'text/plain', 'Skeleton Key - Tree.txt');
| |
− | var treeAsXML = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n' + recursiveGenerateTreeAsXML(productsDetails, 0);
| |
− | printExportLink(treeAsXML, 'Export as XML', 'SCalExportTreeAsXML', 'SCalResourcesExportLinks', 'application/xml', 'Skeleton Key - Tree.xml');
| |
− | | |
− | $('#SCalResetTree').remove();
| |
− | $('#SCalResourcesExportLinks').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);
| |
− | updateLists();
| |
− | }
| |
− | });
| |
− | | |
− | recursiveUpdateTreeProduct(productsDetails);
| |
− | };
| |
− |
| |
− | var printTree = function printTree() {
| |
− | $('#SCalResourcesrow1').append('<h2>Tree of needed products</h2>');
| |
− | $('#SCalResourcesrow1').append('<div id="SCalResourcesExportLinks"></div>');
| |
− |
| |
− | $('#SCalResourcesrow1').append(recursivePrintTreeProduct(productsDetails));
| |
− | recursiveAddHaveChangeEvents(productsDetails);
| |
− | }; | |
− | | |
− | var recursiveAddHaveChangeEvents = function recursiveAddHaveChangeEvents(products) {
| |
− | var setCookie = function(event) {
| |
− | var have = event.target.value;
| |
− | if(!isRealInteger(have)) have = 0;
| |
− | if(have < 0) have = 0;
| |
− | setCookieByInteger(event.target.id, have);
| |
− | updateLists();
| |
− | };
| |
− | for(var values of Object.values(products)) {
| |
− | $('#' + values.id + 'Have').change(setCookie);
| |
− |
| |
− | if(values.hasOwnProperty('subs')) recursiveAddHaveChangeEvents(values.subs);
| |
− | } | |
− | };
| |
− |
| |
− | //Calulates the needed data for the lists and puts them into objects
| |
− | //for further processing
| |
− | var recursiveCalculateResourceData = function recursiveCalculateResourceData(products, multiplikator) {
| |
− | var details = {};
| |
− | for(var [product, values] of Object.entries(products)) {
| |
− | currentProductIdCount += 1;
| |
− | var id = 'SCalItem' + currentProductIdCount;
| |
− | var needed = calculateNeededResource(values.minSkill, values.maxSkill, values.minCount, values.maxCount, values.skill);
| |
− | var needEffective = needed * multiplikator;
| |
− | 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;
| |
− |
| |
− | //set the input field ID, needed amount and resource type
| |
− | details[product] = {
| |
− | id: id,
| |
− | needed: needEffective,
| |
− | type: values.type
| |
− | };
| |
− |
| |
− | // Add needed amount to base resources
| |
− | // we assume that a product without subs is a base resource
| |
− | if(!values.hasOwnProperty('subs')) {
| |
− | if(baseResources.hasOwnProperty(product)) {
| |
− | baseResources[product] = details[product].needed + baseResources[product];
| |
− | } else {
| |
− | baseResources[product] = details[product].needed;
| |
− | }
| |
− | }
| |
− | | |
− | // Add needed amount to to refined resources
| |
− | if(values.type == 'SCalRefinedResource') {
| |
− | if(refinedResources.hasOwnProperty(product)) {
| |
− | refinedResources[product] = details[product].needed + refinedResources[product];
| |
− | } else {
| |
− | refinedResources[product] = details[product].needed;
| |
− | }
| |
− | }
| |
− |
| |
− | // recurse through the sub products
| |
− | if(values.hasOwnProperty('subs')) details[product].subs = recursiveCalculateResourceData(values.subs, details[product].needed);
| |
− | }
| |
− | return details;
| |
− | };
| |
− |
| |
− | // Fills the lists with data
| |
− | var updateLists = function updateLists() {
| |
− | if(validateSkillInputs()) {
| |
− | initialize();
| |
− | updateExportEverythingLinks();
| |
− | updateTree();
| |
− | updateResources(baseResources, 'Base');
| |
− | updateResources(refinedResources, 'Refined');
| |
− | }
| |
− | };
| |
− |
| |
− | //reinitializes everything and calculate needed data
| |
− | var initialize = function initialize() {
| |
− | 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);
| |
− | };
| |
− | | |
− | //creates the contents of the lists
| |
− | var printLists = function printLists() {
| |
− | initialize();
| |
− | | |
− | // Container for the 'Export Everything' features
| |
− | $('#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>'
| |
− | );
| |
− | | |
− | 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' }] });
| |
− | });
| |
− | }
| |
− | };
| |
− | | |
− | var getURL = function getURL(article) {
| |
− | if(hasMW) return mw.util.getUrl(article);
| |
− | return '';
| |
− | };
| |
− | | |
− | var printNeededSkills = function printNeededSkills() {
| |
− | $('#SCalResourcesrow2').append('<h2>Required skills</h2>');
| |
− | | |
− | printExportLink(JSON.stringify(generateSkillsAsJSON()), 'Export as JSON', 'SCalExportSkillsAsJSON', 'SCalResourcesrow2', 'application/json', 'Skeleton Key - Skills.json');
| |
− | printExportLink(generateSkillsAsTEXT(), '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);
| |
− | printExportLink(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 [skill, values] of Object.entries(skillsOverview).sort()) {
| |
− | tablecontent += '<tr>' +
| |
− | '<td><a href="' + getURL(skill) + '">' + skill + '</a></td>' +
| |
− | '<td>' + values.min + '</td>' +
| |
− | '<td>' + values.max + '</td>' +
| |
− | '</tr>';
| |
− | }
| |
− | | |
− | tablecontent += '</tbody>';
| |
− | tablecontent += '</table>';
| |
− | $('#SCalNeededSkillsTable').append(tablecontent);
| |
− | };
| |
− |
| |
− | // Tests if the provided value has a valid integer and sets a cookie for it
| |
− | var setCookieByInteger = function setCookieByInteger(id, value) {
| |
− | if(isRealInteger(value)) setCookie(id, value, new Date(3030,1,4));
| |
− | };
| |
− | | |
− | var recursivePrintTreeProduct = function recursivePrintTreeProduct(products) {
| |
− | var html = '';
| |
− | for(var [product, values] of Object.entries(products)) {
| |
− | html += ' <ul>';
| |
− | html += ' <li>';
| |
− | | |
− | if(product != 'Skeleton Key') {
| |
− | html += ' <span class="' + values.type + '"><a href="' + getURL(product) + '">' + product + '</a> (<span id="' + values.id + 'Needed"></span>) </span>' +
| |
− | ' <label for="' + values.id + 'Have">have:</label><input class="SCalTreeHave" id="' + values.id + 'Have" name="' + values.id + 'Have" type="number" size="8">';
| |
− | } else {
| |
− | html += ' <span class="' + values.type + '"><a href="' + getURL(product) + '">' + product + '</a> (<span id="' + values.id + 'Needed"></span>)</span>';
| |
− | }
| |
− | if(values.hasOwnProperty('subs')) {
| |
− | html += recursivePrintTreeProduct(values.subs);
| |
− | }
| |
− | html += ' </li>';
| |
− | html += ' </ul>';
| |
− | }
| |
− | return html;
| |
− | };
| |
− | | |
− | var recursiveUpdateTreeProduct = function recursiveUpdateTreeProduct(products) {
| |
− | for(var [product, values] of Object.entries(products)) {
| |
− | if(product != 'Skeleton Key') {
| |
− | var greenClass = 'SCalGreenInput';
| |
− | $('#' + values.id + 'Have').removeClass(greenClass);
| |
− | | |
− | var have = getCookie(values.id + 'Have');
| |
− | if(!isRealInteger(have)) have = 0;
| |
− | if(have < 0) have = 0;
| |
− | if(have > 0) $('#' + values.id + 'Have').addClass(greenClass);
| |
− | $('#' + values.id + 'Needed').html(values.needed);
| |
− | $('#' + values.id + 'Have').val(have);
| |
− | } else {
| |
− | $('#' + values.id + 'Needed').html(values.needed);
| |
− | }
| |
− | if(values.hasOwnProperty('subs')) {
| |
− | recursiveUpdateTreeProduct(values.subs);
| |
− | }
| |
− | }
| |
− | };
| |
− | | |
− | var printResources = function printResources(resources, headline, type) {
| |
− | var allDoneId = 'SCal' + type + 'AllDone';
| |
− | $('#SCalResourcesrow2').append('<h2>' + headline + '</h2>');
| |
− | $('#SCalResourcesrow2').append('<div id="SCal' + type + 'ExportLinks"></div>');
| |
− |
| |
− | $('#SCalResourcesrow2').append('<table id="SCal' + type + 'ResourceTable" class="wikitable sortable"><thead>' +
| |
− | '<tr>' +
| |
− | '<th style="vertical-align: top">Resource</th>' +
| |
− | '<th style="vertical-align: top">Need</th>' +
| |
− | '<th style="vertical-align: top" class="unsortable"><span><input type="checkbox" id="' + allDoneId + '"></span></th>' +
| |
− | '</tr></thead>'
| |
− | );
| |
− | | |
− | var tablecontent = '<tbody>';
| |
− | | |
− | for (var resource of Object.keys(resources).sort()) { | |
− | var id = resource.replaceAll(' ','') + 'Need';
| |
− | var doneId = id + 'Done';
| |
− |
| |
− | tablecontent += '<tr>' +
| |
− | '<td><a href="' + getURL(resource) + '">' + resource + '</a></td>' +
| |
− | '<td id="' + id + '"></td>' +
| |
− | '<td style="text-align: center"><input type="checkbox" id="' + doneId + '"></td>' +
| |
− | '</tr>';
| |
− | }
| |
− | | |
− | tablecontent += '</tbody>';
| |
− | tablecontent += '</table>';
| |
− | | |
− | $('#SCal' + type + 'ResourceTable').append(tablecontent);
| |
− |
| |
− | $('#' + allDoneId).click(function() { // handle the 'all done' checkbox
| |
− | if($(this).prop('checked')) {
| |
− | setCookie($(this).get(0).id, true, new Date(3030,1,4));
| |
− |
| |
− | for (var resource of Object.keys(resources)) {
| |
− | var doneId = resource.replaceAll(' ','') + 'NeedDone';
| |
− | setCookie(doneId, true, new Date(3030,1,4));
| |
− | }
| |
− | } else {
| |
− | deleteCookie($(this).get(0).id);
| |
− |
| |
− | for (var resource2 of Object.keys(resources)) {
| |
− | var doneId2 = resource2.replaceAll(' ','') + 'NeedDone';
| |
− | deleteCookie(doneId2);
| |
− | }
| |
− | }
| |
− | updateLists();
| |
− | });
| |
− |
| |
− | for (var res of Object.keys(resources)) { // handle each resource 'done' checkbox
| |
− | var doneId2 = '#' + res.replaceAll(' ','') + 'NeedDone';
| |
− |
| |
− | $(doneId2).change(function() {
| |
− | if($(this).prop('checked')) {
| |
− | setCookie($(this).get(0).id, true, new Date(3030,1,4));
| |
− | updateLists();
| |
− | } else {
| |
− | deleteCookie($(this).get(0).id);
| |
− | updateLists();
| |
− | }
| |
− | });
| |
− | }
| |
− | };
| |
− |
| |
− | var updateResources = function updateResources(resources, type) {
| |
− | var allDoneId = 'SCal' + type + 'AllDone';
| |
− |
| |
− | printExportLink(JSON.stringify(Object.fromEntries(Object.entries(resources).sort())), 'Export as JSON', 'SCalExport' + type + 'resourcesAsJSON', 'SCal' + type + 'ExportLinks', 'application/json', 'Skeleton Key - ' + type + ' Resources.json');
| |
− | printExportLink(generateResourcesAsTEXT(resources), 'Export as TEXT', 'SCalExport' + type + 'resourcesAsTEXT', 'SCal' + type + 'ExportLinks', 'text/plain', 'Skeleton Key - ' + type + ' Resources.txt');
| |
− | var resourceXML = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n' + generateResourcesAsXML(resources, type, 0);
| |
− | printExportLink(resourceXML, 'Export as XML', 'SCalExport' + type + 'resourcesAsXML', 'SCal' + type + 'ExportLinks', 'application/xml', 'Skeleton Key - ' + type + ' Resources.xml');
| |
− |
| |
− | var allDoneChecked = getCookie(allDoneId); // handle the 'All done' checkbox
| |
− | if(allDoneChecked === 'true') {
| |
− | $('#' + allDoneId).prop('checked', true);
| |
− | } else {
| |
− | $('#' + allDoneId).prop('checked', false);
| |
− | }
| |
− |
| |
− | for (var [resource, count] of Object.entries(resources)) { // handle the 'done' checkboxes and set the count accordingly
| |
− | var id = resource.replaceAll(' ', '') + 'Need';
| |
− | var doneId = id + 'Done';
| |
− | var checked = getCookie(doneId);
| |
− |
| |
− | if(checked === 'true') {
| |
− | $('#' + doneId).prop('checked', true);
| |
− | $('#' + id).html(0);
| |
− | } else {
| |
− | $('#' + doneId).prop('checked', false);
| |
− | $('#' + id).html(count);
| |
− | }
| |
− | }
| |
− | };
| |
− |
| |
− | var generateResourcesAsTEXT = function generateResourcesAsTEXT(resources) {
| |
− | var text = '';
| |
− | var length = determineNeededDigitlengthResources(resources);
| |
− | for(var [resource, count] of Object.entries(resources).sort()) {
| |
− | var indent = '';
| |
− | for(var i = 0;i < length - count.toString().length; i++) {
| |
− | indent += ' ';
| |
− | }
| |
− |
| |
− | text += count + indent + ' ' + resource + '\n';
| |
− | }
| |
− | return text;
| |
− | };
| |
− |
| |
− | var generateSkillsAsXML = function generateSkillsAsXML(indent) {
| |
− | var indentation = '';
| |
− | for(var i = 0; i < indent; i++) {
| |
− | indentation += ' ';
| |
− | }
| |
− |
| |
− | var xml = indentation + '<Skills>\n';
| |
− | for(var [skill, values] of Object.entries(skillsOverview).sort()) {
| |
− | xml += indentation + ' <skill minimal="' + values.min + '" optimal="' + values.max + '">' + skill + '</item>\n';
| |
− | }
| |
− | xml += indentation + '</Skills>';
| |
− | return xml;
| |
− | };
| |
− |
| |
− | var generateSkillsAsTEXT = function generateSkillsAsTEXT() {
| |
− | var length = determineNeededDigitlengthSkills();
| |
− | var text = 'Minimal Optimal Skillname\n';
| |
− | text += '--------------------------------------------------------\n';
| |
− | | |
− | for(var [skill, values] of Object.entries(skillsOverview).sort()) {
| |
− | var needIndent = '';
| |
− | var optIndent = '';
| |
− | for(var i = 0;i < length[0] - values.min.toString().length; i++) {
| |
− | needIndent += ' ';
| |
− | }
| |
− | | |
− | for(var j = 0;j < length[1] - values.max.toString().length; j++) {
| |
− | optIndent += ' ';
| |
− | }
| |
− |
| |
− | text += values.min + needIndent + values.max + optIndent + skill + '\n';
| |
− | }
| |
− | return text;
| |
− | };
| |
− | | |
− | var generateResourcesAsXML = function generateResourcesAsXML(resources, tag, indent) {
| |
− | var indentation = '';
| |
− | for(var i = 0; i < indent; i++) {
| |
− | indentation += ' ';
| |
− | }
| |
− | var xml = indentation + '<' + tag + '>\n';
| |
− |
| |
− | for(var [resource, count] of Object.entries(resources).sort()) {
| |
− | xml += indentation + ' <item needed="' + count + '">' + resource + '</item>\n';
| |
− | }
| |
− | xml += '</' + tag + '>\n';
| |
− |
| |
− | return xml;
| |
− | };
| |
− | | |
− | var isRealInteger = function isRealInteger(number) {
| |
− | return (!isNaN(number) && parseInt(Number(number)) == number && !isNaN(parseInt(number, 10)));
| |
− | };
| |
− |
| |
− | // check if the skill's input fields contain valid entries
| |
− | var validateSkillInputs = function validateSkillInputs() {
| |
− | var ready = true;
| |
− | $('#SCalErrorText').html('');
| |
− | | |
− | for (var [skill, values] of Object.entries(skillsOverview)) {
| |
− | var inputObj = $('#' + values.id);
| |
− | var skillCount = inputObj.val();
| |
− |
| |
− | inputObj.removeClass('SCalErroreousInput');
| |
− | inputObj.removeClass('SCalOptimalInput');
| |
− |
| |
− | if (!isRealInteger(skillCount)) {
| |
− | $('#SCalErrorText').append('<center>' + skill + ': this is not a number!</center>');
| |
− | inputObj.addClass('SCalErroreousInput');
| |
− | ready = false;
| |
− | } else {
| |
− | if (skillCount < 1 || skillCount > maxSkillCount) {
| |
− | $('#SCalErrorText').append('<center>' + skill + ': please provide a number between 1 and ' + maxSkillCount + '</center>');
| |
− | inputObj.addClass('SCalErroreousInput');
| |
− | ready = false;
| |
− | }
| |
− |
| |
− | if(skillCount < values.min) {
| |
− | $('#SCalErrorText').append('<center>' + skill + ': Skill ' + skill + ' is too low! (have:' + skillCount + ', need: ' + values.min + ')</center>');
| |
− | inputObj.addClass('SCalErroreousInput');
| |
− | }
| |
− | if(!inputObj.hasClass('SCalErroreousInput')) {
| |
− | if(skillCount >= values.max) inputObj.addClass('SCalOptimalInput');
| |
− | }
| |
− | }
| |
− | }
| |
− | return ready;
| |
− | };
| |
− |
| |
− | var setCookie = function setCookie(name, value, date) {
| |
− | var expires = '; expires=' + date.toGMTString();
| |
− | var cookie = name + '=' + value + expires + cookieParameters;
| |
− | document.cookie = cookie;
| |
− | };
| |
− | | |
− | var getCookie = 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;
| |
− | };
| |
− |
| |
− | var deleteCookie = function deleteCookie(name) {
| |
− | var expires = '; expires=Thu, 01 Jan 1970 00:00:00 UTC';
| |
− | var cookie = name + '=' + expires + cookieParameters;
| |
− | document.cookie = cookie;
| |
− | };
| |
− | // END OF FUNCTIONS
| |
− | ///////////////////////////////////////////////////////////////////
| |
− |
| |
− | // get the css stylesheet
| |
− | // (workaround for htaccess shortlink rules)
| |
− | if(hasMW) {
| |
− | var url = getURL('MediaWiki:SCalScript.css');
| |
− | if(url !== '') {
| |
− | if(url.includes('?')) {
| |
− | url += '&';
| |
− | } else {
| |
− | url += '?';
| |
− | }
| |
− | url += 'action=raw&ctype=text/css';
| |
− | mw.loader.load(url, 'text/css');
| |
− | }
| |
| } | | } |
− |
| |
− | // generate IDs for the tradeskills
| |
− | for(var [key, values] of Object.entries(skillsOverview)) {
| |
− | values.id = 'SCal' + key.replace(/\s/, '') + 'Input';
| |
− | }
| |
− |
| |
− | // Let's start adding content
| |
− | // top part of the gui (static)
| |
− | printHeader();
| |
− | printKeyCounter();
| |
− | printLists();
| |
− |
| |
− | // fill the lists. (dynamic)
| |
− | updateLists();
| |
| } | | } |
− | }); | + | }, 50); |