Difference between revisions of "MediaWiki:SCalScript.js"
From Istaria Lexica
(Big script import) |
(Big script import) |
||
Line 1: | Line 1: | ||
/** | /** | ||
− | * Skeleton Key Calculator | + | * Skeleton Key Calculator |
* Elteria Shadowhand | * Elteria Shadowhand | ||
*/ | */ | ||
$(document).ready(function() { | $(document).ready(function() { | ||
+ | 'use strict'; | ||
if (document.getElementById('SCalMain')) { | if (document.getElementById('SCalMain')) { | ||
− | ' | + | let version = '1.2'; // the script version |
let baseResources; // Collection of the needed base resources. To be filled. | let baseResources; // Collection of the needed base resources. To be filled. | ||
let refinedResources; // Collection of the needed refined resources. To be filled. | let refinedResources; // Collection of the needed refined resources. To be filled. | ||
− | |||
let productsDetails = {}; // detailed dynamic information for each product | let productsDetails = {}; // detailed dynamic information for each product | ||
+ | let currentProductIdCount; // Dirty little helper for adding dynamic IDs to the products | ||
+ | |||
let maxKeyCount = 5000; // Skeleton Key counter maximum value | let maxKeyCount = 5000; // Skeleton Key counter maximum value | ||
let maxSkillCount = 2000; // Skill input fields maximum value | let maxSkillCount = 2000; // Skill input fields maximum value | ||
Line 15: | Line 17: | ||
// This invalidates all URLs and removes other mediawiki specific generated content, i.e. tablesorters and stylesheets | // This invalidates all URLs and removes other mediawiki specific generated content, i.e. tablesorters and stylesheets | ||
let hasMW = true; | let hasMW = true; | ||
− | |||
let cookieParameters = '; path=/; SameSite=Strict'; | let cookieParameters = '; path=/; SameSite=Strict'; | ||
− | // The dataset | + | // The dataset of resources |
// Note that the skill is NOT the actual skill you need to create the product! | // Note that the skill is NOT the actual skill you need to create the product! | ||
− | // It | + | // It's the skill you would need to create its parent product. E.g. if you created a Spell Shard which originally |
− | // the subcomponent (i.e. a Stone Brick, which would be Stoneworking) of it would be Spellcraft. The min and max | + | // 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. | // the REAL needed skills are documented in the skillsOverview variable. | ||
let products = { | let products = { | ||
Line 123: | Line 124: | ||
'Thornwood Bowl': { skill: 'Alchemy', minCount: 1, maxCount: 3, 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 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 } | 'Thornwood Log': { skill: 'Lumbering', minCount: 2, maxCount: 5, minSkill: 1000, maxSkill: 1325 } | ||
Line 132: | Line 133: | ||
}, 'Crystallized Travertine Brick': { skill: 'Alchemy', minCount: 5, maxCount: 10, minSkill: 1000, maxSkill: 1325, type: 'SCalRefinedResource', subs: | }, '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 } | + | '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: | }, 'Purified Radiant Essence Orb': { skill: 'Alchemy', minCount: 1, maxCount: 4, minSkill: 1100, maxSkill: 1425, type: 'SCalRefinedResource', subs: | ||
Line 147: | Line 148: | ||
}, 'Adamantium-Mithril Bar': { skill: 'Tinkering', minCount: 1, maxCount: 1, minSkill: 1100, maxSkill: 1425, type: 'SCalRefinedResource', subs: | }, '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 } | + | 'Mithril Ore': { skill: 'Smelting', minCount: 3, maxCount: 6, minSkill: 1100, maxSkill: 1425 } , |
− | + | 'Adamantium Ore': { skill: 'Smelting', minCount: 3, maxCount: 6, minSkill: 1100, maxSkill: 1425 } | |
} | } | ||
} | } | ||
Line 156: | Line 157: | ||
}, 'Skeleton Key Mold': { skill: 'Tinkering', minCount: 1, maxCount: 1, minSkill: 1100, maxSkill: 1425, type: 'SCalProduct', subs: | }, '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 } | + | '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 } | |
} | } | ||
} | } | ||
Line 164: | Line 165: | ||
}; | }; | ||
− | // | + | // A list of the skills and their limits you need to be able to craft everything |
− | skillsOverview = { | + | let skillsOverview = { |
'Tinkering': { min: 1100, max: 1425 }, | 'Tinkering': { min: 1100, max: 1425 }, | ||
'Scribing': { min: 1100, max: 1425 }, | 'Scribing': { min: 1100, max: 1425 }, | ||
Line 180: | Line 181: | ||
}; | }; | ||
− | // | + | /////////////////////////////////////////////////////////////////// |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
//FUNCTIONS: | //FUNCTIONS: | ||
− | function printKeyCounter() { | + | 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><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>'); | $('#SCalMain').append('<center><input type="number" id="SCalCountInput" name="SCalCountInput" value="1" size="6"></center>'); | ||
Line 211: | Line 196: | ||
updateLists(); | updateLists(); | ||
}); | }); | ||
− | } | + | }; |
− | function printHeader() { | + | 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('<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>Please enter your current skills in the fields below. Defaults to optimal skills.</center>'); | ||
Line 228: | Line 218: | ||
$('#SCalMain').append('<center><div id="SCalSkills" style="column-count:3; width:fit-content; text-align:left"></div></center>'); | $('#SCalMain').append('<center><div id="SCalSkills" style="column-count:3; width:fit-content; text-align:left"></div></center>'); | ||
− | for (var [ | + | for (var [skillName, values] of Object.entries(skillsOverview)) { |
− | var id = | + | var id = values.id; |
− | var | + | var skillLevel = getCookie(id); |
− | if(!isRealInteger( | + | |
+ | 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="' + | + | $('#SCalSkills').append('<input type="number" id="' + id + '" name="' + id + '" size="6" value="' + skillLevel + '"><label for="' + id + '"> ' + skillName + '</label><br />'); |
− | $('#' + id).change( | + | $('#' + id).change(setCookie); |
− | |||
− | |||
− | |||
$('#' + id).change(); | $('#' + id).change(); | ||
} | } | ||
Line 247: | Line 235: | ||
$('#SCalMain').append('<br />'); | $('#SCalMain').append('<br />'); | ||
$('#SCalMain').append('<hr />'); | $('#SCalMain').append('<hr />'); | ||
− | } | + | }; |
− | function printExportLink(text, buttontext, id, mainId, mimetype, filename) { | + | var printExportLink = function printExportLink(text, buttontext, id, mainId, mimetype, filename) { |
$('#' + id).remove(); | $('#' + id).remove(); | ||
$('#' + mainId).append('<a id="' + id + 'A"></a>'); | $('#' + mainId).append('<a id="' + id + 'A"></a>'); | ||
Line 258: | Line 246: | ||
$('#' + id + 'A').attr('download', filename); | $('#' + id + 'A').attr('download', filename); | ||
}); | }); | ||
− | } | + | }; |
// Creates a simple products object, containing the needed resources | // Creates a simple products object, containing the needed resources | ||
− | function recursiveCreateProductTree( | + | var recursiveCreateProductTree = function recursiveCreateProductTree(products) { |
− | var | + | var treeProducts = {}; |
− | for(var [ | + | for(var [product, values] of Object.entries(products)) { |
− | product | + | treeProducts[product] = { needed: values.needed }; |
− | if(values.hasOwnProperty('subs')) product | + | if(values.hasOwnProperty('subs')) treeProducts[product].subs = recursiveCreateProductTree(values.subs); |
} | } | ||
− | return | + | return treeProducts; |
− | } | + | }; |
− | function updateExportEverythingLinks() { | + | var updateExportEverythingLinks = function updateExportEverythingLinks() { |
var JSONeverything = JSON.stringify({ | var JSONeverything = JSON.stringify({ | ||
'Product Tree': recursiveCreateProductTree(productsDetails), | 'Product Tree': recursiveCreateProductTree(productsDetails), | ||
Line 290: | Line 278: | ||
TEXTeverything += '\n\n'; | TEXTeverything += '\n\n'; | ||
TEXTeverything += '--- Needed Skills ---\n\n'; | TEXTeverything += '--- Needed Skills ---\n\n'; | ||
− | TEXTeverything += generateSkillsAsTEXT( | + | TEXTeverything += generateSkillsAsTEXT(); |
printExportLink(TEXTeverything, 'Export everything as TEXT', 'SCalExportEverythingAsEXT', 'SCalExportEverything', 'text/plain', 'Skeleton Key - All.txt'); | printExportLink(TEXTeverything, 'Export everything as TEXT', 'SCalExportEverythingAsEXT', 'SCalExportEverything', 'text/plain', 'Skeleton Key - All.txt'); | ||
Line 297: | Line 285: | ||
XMLeverything += recursiveGenerateTreeAsXML(productsDetails, 2); | XMLeverything += recursiveGenerateTreeAsXML(productsDetails, 2); | ||
XMLeverything += '</ResourcesTree>\n'; | XMLeverything += '</ResourcesTree>\n'; | ||
− | XMLeverything += generateResourcesAsXML(baseResources,'BaseResources', 0); | + | XMLeverything += generateResourcesAsXML(baseResources, 'BaseResources', 0); |
− | XMLeverything += generateResourcesAsXML(refinedResources,'RefinedResources', 0); | + | XMLeverything += generateResourcesAsXML(refinedResources, 'RefinedResources', 0); |
XMLeverything += generateSkillsAsXML(0); | XMLeverything += generateSkillsAsXML(0); | ||
printExportLink(XMLeverything, 'Export everything as XML', 'SCalExportEverythingAsXML', 'SCalExportEverything', 'application/xml', 'Skeleton Key - All.xml'); | printExportLink(XMLeverything, 'Export everything as XML', 'SCalExportEverythingAsXML', 'SCalExportEverything', 'application/xml', 'Skeleton Key - All.xml'); | ||
− | } | + | }; |
− | function generateSkillsAsJSON() { | + | var generateSkillsAsJSON = function generateSkillsAsJSON() { |
var json = {}; | var json = {}; | ||
− | for(var [ | + | for(var [skill, values] of Object.entries(skillsOverview).sort()) { |
− | json[ | + | json[skill] = { |
minimal: values.min, | minimal: values.min, | ||
optimal: values.max | optimal: values.max | ||
Line 312: | Line 300: | ||
} | } | ||
return json; | return json; | ||
− | } | + | }; |
− | function recursiveGenerateTreeAsTEXT(productsDetails, indent) { | + | var recursiveGenerateTreeAsTEXT = function recursiveGenerateTreeAsTEXT(productsDetails, indent) { |
var text = ''; | var text = ''; | ||
− | for(var [ | + | for(var [product, values] of Object.entries(productsDetails)) { |
for(var i=0; i<indent; i++) { | for(var i=0; i<indent; i++) { | ||
text += ' '; | text += ' '; | ||
Line 322: | Line 310: | ||
text += '|- '; | text += '|- '; | ||
− | text += | + | text += product + ' '; |
− | text += | + | text += values.needed; |
text += '\n'; | text += '\n'; | ||
− | if( | + | if(values.hasOwnProperty('subs')) { |
− | text += recursiveGenerateTreeAsTEXT( | + | text += recursiveGenerateTreeAsTEXT(values.subs, indent + 3); |
} | } | ||
} | } | ||
return text; | return text; | ||
− | } | + | }; |
− | function recursiveGenerateTreeAsXML(productsDetails, indent) { | + | var recursiveGenerateTreeAsXML = function recursiveGenerateTreeAsXML(productsDetails, indent) { |
var xml = ''; | var xml = ''; | ||
− | for(var [ | + | for(var [product, value] of Object.entries(productsDetails)) { |
− | for(var i=0; i<indent; i++) { | + | for(var i=0; i < indent; i++) { |
xml += ' '; | xml += ' '; | ||
} | } | ||
− | xml += '<item name="' + | + | xml += '<item name="' + product +'" needed="' + value.needed + '">\n'; |
− | if(value.hasOwnProperty('subs')) xml += recursiveGenerateTreeAsXML(value.subs, indent+2); | + | if(value.hasOwnProperty('subs')) xml += recursiveGenerateTreeAsXML(value.subs, indent + 2); |
− | for(var | + | for(var j=0; j < indent; j++) { |
xml += ' '; | xml += ' '; | ||
} | } | ||
Line 351: | Line 339: | ||
} | } | ||
return xml; | return xml; | ||
− | } | + | }; |
− | function | + | var determineNeededDigitlengthResources = function determineNeededDigitlengthResources(resources) { |
− | for(var | + | var length = 0; |
− | if( | + | for(var count of Object.values(resources)) { |
+ | if(count.toString().length > length) length = count.toString().length; | ||
} | } | ||
return length; | return length; | ||
− | } | + | }; |
− | function determineNeededDigitlengthSkills( | + | var determineNeededDigitlengthSkills = function determineNeededDigitlengthSkills() { |
− | for(var | + | var length = [9, 10]; |
− | if(skillsOverview[ | + | for(var skill of Object.keys(skillsOverview)) { |
− | if(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; | return length; | ||
− | } | + | }; |
− | // | + | // calculates the effective resources needed |
− | function calculateNeededResource(minSkill, maxSkill, minCount, maxCount, skill) { | + | var calculateNeededResource = function calculateNeededResource(minSkill, maxSkill, minCount, maxCount, skill) { |
var currentSkill = $('#' + skillsOverview[skill].id).val(); | var currentSkill = $('#' + skillsOverview[skill].id).val(); | ||
Line 380: | Line 370: | ||
return needEffective; | return needEffective; | ||
− | } | + | }; |
− | function recursiveCleanHaveCookies(details) { | + | var recursiveCleanHaveCookies = function recursiveCleanHaveCookies(details) { |
for(var values of Object.values(details)) { | for(var values of Object.values(details)) { | ||
var id = values.id + 'Have'; | var id = values.id + 'Have'; | ||
Line 389: | Line 379: | ||
if(values.hasOwnProperty('subs')) recursiveCleanHaveCookies(values.subs); | if(values.hasOwnProperty('subs')) recursiveCleanHaveCookies(values.subs); | ||
} | } | ||
− | } | + | }; |
− | function updateTree() { | + | var updateTree = function updateTree() { |
printExportLink(JSON.stringify(recursiveCreateProductTree(productsDetails)), 'Export as JSON', 'SCalExportTreeAsJSON', 'SCalResourcesExportLinks', 'application/json', 'Skeleton Key - Tree.json'); | 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'); | printExportLink(recursiveGenerateTreeAsTEXT(productsDetails, 0), 'Export as TEXT', 'SCalExportTreeAsTEXT', 'SCalResourcesExportLinks', 'text/plain', 'Skeleton Key - Tree.txt'); | ||
Line 407: | Line 397: | ||
recursiveUpdateTreeProduct(productsDetails); | recursiveUpdateTreeProduct(productsDetails); | ||
− | } | + | }; |
− | function printTree() { | + | var printTree = function printTree() { |
$('#SCalResourcesrow1').append('<h2>Tree of needed products</h2>'); | $('#SCalResourcesrow1').append('<h2>Tree of needed products</h2>'); | ||
$('#SCalResourcesrow1').append('<div id="SCalResourcesExportLinks"></div>'); | $('#SCalResourcesrow1').append('<div id="SCalResourcesExportLinks"></div>'); | ||
Line 415: | Line 405: | ||
$('#SCalResourcesrow1').append(recursivePrintTreeProduct(productsDetails)); | $('#SCalResourcesrow1').append(recursivePrintTreeProduct(productsDetails)); | ||
recursiveAddHaveChangeEvents(productsDetails); | recursiveAddHaveChangeEvents(productsDetails); | ||
− | } | + | }; |
− | function recursiveAddHaveChangeEvents( | + | var recursiveAddHaveChangeEvents = function recursiveAddHaveChangeEvents(products) { |
− | + | var setCookie = function(event) { | |
− | |||
var have = event.target.value; | var have = event.target.value; | ||
if(!isRealInteger(have)) have = 0; | if(!isRealInteger(have)) have = 0; | ||
Line 425: | Line 414: | ||
setCookieByInteger(event.target.id, have); | setCookieByInteger(event.target.id, have); | ||
updateLists(); | updateLists(); | ||
− | }); | + | }; |
+ | for(var values of Object.values(products)) { | ||
+ | $('#' + values.id + 'Have').change(setCookie); | ||
if(values.hasOwnProperty('subs')) recursiveAddHaveChangeEvents(values.subs); | if(values.hasOwnProperty('subs')) recursiveAddHaveChangeEvents(values.subs); | ||
} | } | ||
− | } | + | }; |
//Calulates the needed data for the lists and puts them into objects | //Calulates the needed data for the lists and puts them into objects | ||
//for further processing | //for further processing | ||
− | function recursiveCalculateResourceData(products, | + | var recursiveCalculateResourceData = function recursiveCalculateResourceData(products, multiplikator) { |
var details = {}; | var details = {}; | ||
− | for(var [ | + | for(var [product, values] of Object.entries(products)) { |
currentProductIdCount += 1; | currentProductIdCount += 1; | ||
var id = 'SCalItem' + currentProductIdCount; | var id = 'SCalItem' + currentProductIdCount; | ||
var needed = calculateNeededResource(values.minSkill, values.maxSkill, values.minCount, values.maxCount, values.skill); | var needed = calculateNeededResource(values.minSkill, values.maxSkill, values.minCount, values.maxCount, values.skill); | ||
− | needEffective = needed * | + | var needEffective = needed * multiplikator; |
var have = getCookie(id + 'Have'); | var have = getCookie(id + 'Have'); | ||
if(!isRealInteger(have)) have = 0; | if(!isRealInteger(have)) have = 0; | ||
Line 446: | Line 437: | ||
if(needEffective < 0) needEffective = 0; | if(needEffective < 0) needEffective = 0; | ||
− | // | + | //set the input field ID, needed amount and resource type |
− | + | details[product] = { | |
− | details[ | ||
id: id, | id: id, | ||
needed: needEffective, | needed: needEffective, | ||
Line 454: | Line 444: | ||
}; | }; | ||
− | // Add to base resources | + | // Add needed amount to base resources |
// we assume that a product without subs is a base resource | // we assume that a product without subs is a base resource | ||
if(!values.hasOwnProperty('subs')) { | if(!values.hasOwnProperty('subs')) { | ||
− | if(baseResources.hasOwnProperty( | + | if(baseResources.hasOwnProperty(product)) { |
− | baseResources[ | + | baseResources[product] = details[product].needed + baseResources[product]; |
} else { | } else { | ||
− | baseResources[ | + | baseResources[product] = details[product].needed; |
} | } | ||
} | } | ||
− | // Add to refined resources | + | // Add needed amount to to refined resources |
if(values.type == 'SCalRefinedResource') { | if(values.type == 'SCalRefinedResource') { | ||
− | if(refinedResources.hasOwnProperty( | + | if(refinedResources.hasOwnProperty(product)) { |
− | refinedResources[ | + | refinedResources[product] = details[product].needed + refinedResources[product]; |
} else { | } else { | ||
− | refinedResources[ | + | refinedResources[product] = details[product].needed; |
} | } | ||
} | } | ||
// recurse through the sub products | // recurse through the sub products | ||
− | if(values.hasOwnProperty('subs')) details[ | + | if(values.hasOwnProperty('subs')) details[product].subs = recursiveCalculateResourceData(values.subs, details[product].needed); |
} | } | ||
return details; | return details; | ||
− | } | + | }; |
// Fills the lists with data | // Fills the lists with data | ||
− | function updateLists() { | + | var updateLists = function updateLists() { |
if(validateSkillInputs()) { | if(validateSkillInputs()) { | ||
initialize(); | initialize(); | ||
Line 488: | Line 478: | ||
updateResources(refinedResources, 'Refined'); | updateResources(refinedResources, 'Refined'); | ||
} | } | ||
− | } | + | }; |
− | //reinitializes everything and | + | //reinitializes everything and calculate needed data |
− | function initialize() { | + | var initialize = function initialize() { |
baseResources = {}; | baseResources = {}; | ||
refinedResources = {}; | refinedResources = {}; | ||
Line 502: | Line 492: | ||
productsDetails = recursiveCalculateResourceData(products, keysWanted); | productsDetails = recursiveCalculateResourceData(products, keysWanted); | ||
− | } | + | }; |
//creates the contents of the lists | //creates the contents of the lists | ||
− | function printLists() { | + | var printLists = function printLists() { |
initialize(); | initialize(); | ||
− | // Container for the 'Export Everything' | + | // Container for the 'Export Everything' features |
$('#SCalMain').append('<div id="SCalExportEverything" style="text-align: center"></div>'); | $('#SCalMain').append('<div id="SCalExportEverything" style="text-align: center"></div>'); | ||
// Containers for the lists | // Containers for the lists | ||
− | $('#SCalMain').append('<center><table>' | + | $('#SCalMain').append('<center><table>' + |
− | + | '<tr valign="top">' + | |
− | + | '<td id="SCalResourcesrow1"></td>' + | |
− | + | '<td id="SCalResourcesrow2"></td>' + | |
− | + | '</tr>' + | |
− | + | '</table></center>' | |
); | ); | ||
Line 527: | Line 517: | ||
if(hasMW) { | if(hasMW) { | ||
mw.loader.using('jquery.tablesorter', function() { // add the tablesorter to tables | mw.loader.using('jquery.tablesorter', function() { // add the tablesorter to tables | ||
− | $('table.sortable').tablesorter({ sortList: [{ 0: 'asc' }] }) | + | $('table.sortable').tablesorter({ sortList: [{ 0: 'asc' }] }); |
}); | }); | ||
} | } | ||
− | + | }; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | function getURL(article) { | + | var getURL = function getURL(article) { |
if(hasMW) return mw.util.getUrl(article); | if(hasMW) return mw.util.getUrl(article); | ||
return ''; | return ''; | ||
− | } | + | }; |
− | function printNeededSkills() { | + | var printNeededSkills = function printNeededSkills() { |
$('#SCalResourcesrow2').append('<h2>Required skills</h2>'); | $('#SCalResourcesrow2').append('<h2>Required skills</h2>'); | ||
− | printExportLink(JSON.stringify( | + | printExportLink(JSON.stringify(generateSkillsAsJSON()), 'Export as JSON', 'SCalExportSkillsAsJSON', 'SCalResourcesrow2', 'application/json', 'Skeleton Key - Skills.json'); |
− | printExportLink(generateSkillsAsTEXT( | + | 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); | 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'); | printExportLink(skillsAsXML, 'Export as XML', 'SCalExportSkillsAsXML', 'SCalResourcesrow2', 'application/xml', 'Skeleton Key - Skills.xml'); | ||
− | $('#SCalResourcesrow2').append('<table id="SCalNeededSkillsTable" class="wikitable sortable"><thead>' | + | $('#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>'; | var tablecontent = '<tbody>'; | ||
− | for(var [ | + | for(var [skill, values] of Object.entries(skillsOverview).sort()) { |
− | tablecontent += '<tr>' | + | tablecontent += '<tr>' + |
− | + | '<td><a href="' + getURL(skill) + '">' + skill + '</a></td>' + | |
− | + | '<td>' + values.min + '</td>' + | |
− | + | '<td>' + values.max + '</td>' + | |
− | + | '</tr>'; | |
} | } | ||
Line 578: | Line 556: | ||
tablecontent += '</table>'; | tablecontent += '</table>'; | ||
$('#SCalNeededSkillsTable').append(tablecontent); | $('#SCalNeededSkillsTable').append(tablecontent); | ||
− | } | + | }; |
// Tests if the provided value has a valid integer and sets a cookie for it | // Tests if the provided value has a valid integer and sets a cookie for it | ||
− | function setCookieByInteger(id, value) { | + | var setCookieByInteger = function setCookieByInteger(id, value) { |
if(isRealInteger(value)) setCookie(id, value, new Date(3030,1,4)); | if(isRealInteger(value)) setCookie(id, value, new Date(3030,1,4)); | ||
− | } | + | }; |
− | function recursivePrintTreeProduct( | + | var recursivePrintTreeProduct = function recursivePrintTreeProduct(products) { |
var html = ''; | var html = ''; | ||
− | for(var [ | + | for(var [product, values] of Object.entries(products)) { |
html += ' <ul>'; | html += ' <ul>'; | ||
html += ' <li>'; | html += ' <li>'; | ||
− | if( | + | if(product != 'Skeleton Key') { |
− | html += ' <span class="' + values.type + '"><a href="' + getURL( | + | 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 { | } else { | ||
− | html += ' <span class="' + values.type + '"><a href="' + getURL( | + | html += ' <span class="' + values.type + '"><a href="' + getURL(product) + '">' + product + '</a> (<span id="' + values.id + 'Needed"></span>)</span>'; |
} | } | ||
if(values.hasOwnProperty('subs')) { | if(values.hasOwnProperty('subs')) { | ||
Line 604: | Line 582: | ||
} | } | ||
return html; | return html; | ||
− | } | + | }; |
− | function recursiveUpdateTreeProduct( | + | var recursiveUpdateTreeProduct = function recursiveUpdateTreeProduct(products) { |
− | for(var [ | + | for(var [product, values] of Object.entries(products)) { |
− | if( | + | if(product != 'Skeleton Key') { |
var greenClass = 'SCalGreenInput'; | var greenClass = 'SCalGreenInput'; | ||
$('#' + values.id + 'Have').removeClass(greenClass); | $('#' + values.id + 'Have').removeClass(greenClass); | ||
Line 625: | Line 603: | ||
} | } | ||
} | } | ||
− | } | + | }; |
− | function printResources(resources, headline, type) { | + | var printResources = function printResources(resources, headline, type) { |
+ | var allDoneId = 'SCal' + type + 'AllDone'; | ||
$('#SCalResourcesrow2').append('<h2>' + headline + '</h2>'); | $('#SCalResourcesrow2').append('<h2>' + headline + '</h2>'); | ||
$('#SCalResourcesrow2').append('<div id="SCal' + type + 'ExportLinks"></div>'); | $('#SCalResourcesrow2').append('<div id="SCal' + type + 'ExportLinks"></div>'); | ||
− | $('#SCalResourcesrow2').append('<table id="SCal' + type + 'ResourceTable" class="wikitable sortable"><thead>' | + | $('#SCalResourcesrow2').append('<table id="SCal' + type + 'ResourceTable" class="wikitable sortable"><thead>' + |
− | + | '<tr>' + | |
− | + | '<th style="vertical-align: top">Resource</th>' + | |
− | + '<th align=" | + | '<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>'; | var tablecontent = '<tbody>'; | ||
− | for (var | + | for (var resource of Object.keys(resources).sort()) { |
− | var id = | + | var id = resource.replaceAll(' ','') + 'Need'; |
+ | var doneId = id + 'Done'; | ||
− | tablecontent += '<tr>' | + | tablecontent += '<tr>' + |
− | + | '<td><a href="' + getURL(resource) + '">' + resource + '</a></td>' + | |
− | + | '<td id="' + id + '"></td>' + | |
− | + '</tr>'; | + | '<td style="text-align: center"><input type="checkbox" id="' + doneId + '"></td>' + |
+ | '</tr>'; | ||
} | } | ||
Line 653: | Line 635: | ||
$('#SCal' + type + 'ResourceTable').append(tablecontent); | $('#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(); | ||
+ | } | ||
+ | }); | ||
+ | } | ||
+ | }; | ||
− | function updateResources(resources, type) { | + | 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(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'); | printExportLink(generateResourcesAsTEXT(resources), 'Export as TEXT', 'SCalExport' + type + 'resourcesAsTEXT', 'SCal' + type + 'ExportLinks', 'text/plain', 'Skeleton Key - ' + type + ' Resources.txt'); | ||
Line 661: | Line 678: | ||
printExportLink(resourceXML, 'Export as XML', 'SCalExport' + type + 'resourcesAsXML', 'SCal' + type + 'ExportLinks', 'application/xml', 'Skeleton Key - ' + type + ' Resources.xml'); | 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); | ||
+ | } | ||
+ | } | ||
+ | }; | ||
− | function generateResourcesAsTEXT(resources) { | + | var generateResourcesAsTEXT = function generateResourcesAsTEXT(resources) { |
var text = ''; | var text = ''; | ||
− | var length = | + | var length = determineNeededDigitlengthResources(resources); |
− | for(var [ | + | for(var [resource, count] of Object.entries(resources).sort()) { |
var indent = ''; | var indent = ''; | ||
for(var i = 0;i < length - count.toString().length; i++) { | for(var i = 0;i < length - count.toString().length; i++) { | ||
Line 676: | Line 709: | ||
} | } | ||
− | text += count + indent + ' ' + | + | text += count + indent + ' ' + resource + '\n'; |
} | } | ||
return text; | return text; | ||
− | } | + | }; |
− | function generateSkillsAsXML(indent) { | + | var generateSkillsAsXML = function generateSkillsAsXML(indent) { |
− | indentation = ''; | + | var indentation = ''; |
for(var i = 0; i < indent; i++) { | for(var i = 0; i < indent; i++) { | ||
indentation += ' '; | indentation += ' '; | ||
Line 688: | Line 721: | ||
var xml = indentation + '<Skills>\n'; | var xml = indentation + '<Skills>\n'; | ||
− | for(var [ | + | for(var [skill, values] of Object.entries(skillsOverview).sort()) { |
− | xml += indentation + ' <skill minimal="' + values.min + '" optimal="' + values.max + '">' + | + | xml += indentation + ' <skill minimal="' + values.min + '" optimal="' + values.max + '">' + skill + '</item>\n'; |
} | } | ||
− | xml += indentation + '</Skills>' | + | xml += indentation + '</Skills>'; |
return xml; | return xml; | ||
− | } | + | }; |
− | function generateSkillsAsTEXT( | + | var generateSkillsAsTEXT = function generateSkillsAsTEXT() { |
+ | var length = determineNeededDigitlengthSkills(); | ||
var text = 'Minimal Optimal Skillname\n'; | var text = 'Minimal Optimal Skillname\n'; | ||
text += '--------------------------------------------------------\n'; | text += '--------------------------------------------------------\n'; | ||
− | for(var [ | + | for(var [skill, values] of Object.entries(skillsOverview).sort()) { |
var needIndent = ''; | var needIndent = ''; | ||
var optIndent = ''; | var optIndent = ''; | ||
− | for(var i = 0;i < | + | for(var i = 0;i < length[0] - values.min.toString().length; i++) { |
needIndent += ' '; | needIndent += ' '; | ||
} | } | ||
− | for(var | + | for(var j = 0;j < length[1] - values.max.toString().length; j++) { |
optIndent += ' '; | optIndent += ' '; | ||
} | } | ||
− | text += values.min + needIndent + values.max + optIndent + | + | text += values.min + needIndent + values.max + optIndent + skill + '\n'; |
} | } | ||
return text; | return text; | ||
− | } | + | }; |
− | function generateResourcesAsXML(resources, tag, indent) { | + | var generateResourcesAsXML = function generateResourcesAsXML(resources, tag, indent) { |
var indentation = ''; | var indentation = ''; | ||
for(var i = 0; i < indent; i++) { | for(var i = 0; i < indent; i++) { | ||
Line 722: | Line 756: | ||
var xml = indentation + '<' + tag + '>\n'; | var xml = indentation + '<' + tag + '>\n'; | ||
− | for(var [ | + | for(var [resource, count] of Object.entries(resources).sort()) { |
− | xml += indentation + ' <item needed="' + count + '">' + | + | xml += indentation + ' <item needed="' + count + '">' + resource + '</item>\n'; |
} | } | ||
xml += '</' + tag + '>\n'; | xml += '</' + tag + '>\n'; | ||
+ | |||
return xml; | return xml; | ||
− | } | + | }; |
− | function isRealInteger(number) { | + | var isRealInteger = function isRealInteger(number) { |
return (!isNaN(number) && parseInt(Number(number)) == number && !isNaN(parseInt(number, 10))); | return (!isNaN(number) && parseInt(Number(number)) == number && !isNaN(parseInt(number, 10))); | ||
− | } | + | }; |
// check if the skill's input fields contain valid entries | // check if the skill's input fields contain valid entries | ||
− | function validateSkillInputs() { | + | var validateSkillInputs = function validateSkillInputs() { |
var ready = true; | var ready = true; | ||
$('#SCalErrorText').html(''); | $('#SCalErrorText').html(''); | ||
Line 740: | Line 775: | ||
for (var [skill, values] of Object.entries(skillsOverview)) { | for (var [skill, values] of Object.entries(skillsOverview)) { | ||
var inputObj = $('#' + values.id); | var inputObj = $('#' + values.id); | ||
+ | var skillCount = inputObj.val(); | ||
inputObj.removeClass('SCalErroreousInput'); | inputObj.removeClass('SCalErroreousInput'); | ||
inputObj.removeClass('SCalOptimalInput'); | inputObj.removeClass('SCalOptimalInput'); | ||
− | + | if (!isRealInteger(skillCount)) { | |
− | |||
− | if (!isRealInteger( | ||
$('#SCalErrorText').append('<center>' + skill + ': this is not a number!</center>'); | $('#SCalErrorText').append('<center>' + skill + ': this is not a number!</center>'); | ||
inputObj.addClass('SCalErroreousInput'); | inputObj.addClass('SCalErroreousInput'); | ||
ready = false; | ready = false; | ||
} else { | } else { | ||
− | if ( | + | if (skillCount < 1 || skillCount > maxSkillCount) { |
$('#SCalErrorText').append('<center>' + skill + ': please provide a number between 1 and ' + maxSkillCount + '</center>'); | $('#SCalErrorText').append('<center>' + skill + ': please provide a number between 1 and ' + maxSkillCount + '</center>'); | ||
inputObj.addClass('SCalErroreousInput'); | inputObj.addClass('SCalErroreousInput'); | ||
Line 757: | Line 791: | ||
} | } | ||
− | if( | + | if(skillCount < values.min) { |
− | $('#SCalErrorText').append('<center>' + skill + ': Skill ' + skill + ' is too low! (have:' + | + | $('#SCalErrorText').append('<center>' + skill + ': Skill ' + skill + ' is too low! (have:' + skillCount + ', need: ' + values.min + ')</center>'); |
inputObj.addClass('SCalErroreousInput'); | inputObj.addClass('SCalErroreousInput'); | ||
} | } | ||
if(!inputObj.hasClass('SCalErroreousInput')) { | if(!inputObj.hasClass('SCalErroreousInput')) { | ||
− | if( | + | if(skillCount >= values.max) inputObj.addClass('SCalOptimalInput'); |
} | } | ||
} | } | ||
} | } | ||
return ready; | return ready; | ||
− | } | + | }; |
− | function setCookie(name, value, date) { | + | var setCookie = function setCookie(name, value, date) { |
var expires = '; expires=' + date.toGMTString(); | var expires = '; expires=' + date.toGMTString(); | ||
var cookie = name + '=' + value + expires + cookieParameters; | var cookie = name + '=' + value + expires + cookieParameters; | ||
document.cookie = cookie; | document.cookie = cookie; | ||
− | } | + | }; |
− | function getCookie(name) { | + | var getCookie = function getCookie(name) { |
var nameEQ = name + '='; | var nameEQ = name + '='; | ||
var ca = document.cookie.split(';'); | var ca = document.cookie.split(';'); | ||
Line 781: | Line 815: | ||
var c = ca[i]; | var c = ca[i]; | ||
while (c.charAt(0)==' ') c = c.substring(1,c.length); | while (c.charAt(0)==' ') c = c.substring(1,c.length); | ||
− | if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); | + | if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length,c.length); |
} | } | ||
return null; | return null; | ||
− | } | + | }; |
− | function deleteCookie(name) { | + | var deleteCookie = function deleteCookie(name) { |
var expires = '; expires=Thu, 01 Jan 1970 00:00:00 UTC'; | var expires = '; expires=Thu, 01 Jan 1970 00:00:00 UTC'; | ||
var cookie = name + '=' + expires + cookieParameters; | var cookie = name + '=' + expires + cookieParameters; | ||
document.cookie = cookie; | 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(); | ||
} | } | ||
}); | }); |
Revision as of 21:34, 1 July 2022
/** * Skeleton Key Calculator * Elteria Shadowhand */ $(document).ready(function() { 'use strict'; if (document.getElementById('SCalMain')) { let version = '1.2'; // the script version let baseResources; // Collection of the needed base resources. To be filled. let refinedResources; // Collection of the needed refined resources. To be filled. let productsDetails = {}; // detailed dynamic information for each product let currentProductIdCount; // Dirty little helper for adding dynamic IDs to the products 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(); } });