In this tutorial, I’ll discuss about Vorici Calculator. At first, we’ll look at What is a Vorici Calculator? and How it works? After that, I’ll guide you on how to create a Vorici Chromatic Calculator using HTML5 and JavaScript.
The complete source code of the Vorici Calc is available in this article. So, keep reading, and let’s get started.
What is a Vorici Calculator?
Vorici Calculator is a tool that enables us to find the average cost, craft type, success chance, and standard deviation of Chromatic Orb.
Basically, Vorici is a Master Assassin in Path of Exile video game. Whereas Chromatic Orb is a currency item that allows us to re-roll the color of sockets on a weapon or piece of armour.
This tool is sometimes also referred as poe chromatic calculator.
Some of Vorici calc notable features include displaying cost per try and average attempts (mean) in chromatics.
How does Vorici Chromatic Calculator Works?
The Vorici Chrome calculator provides the cost of various crafts by following a simple three step procedure:
Input:-
- Enter the total number of sockets.
- Substitute the requirements and desired color of sockets in RGB colors.
- Click the “Calculate” button to get the results.
Output:-
- The vorici chromatic calculator displays the crafting table and experience progression for sockets of the chromatic orb.
Project Files
Create a new folder on your computer and then add these files in it.
- index.html
- style.css
- vorici_calculator.js
- SortTable.min.js – (This file is an open source library for JavaScript client-side sortable tables)
Vorici Calc Source Code
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"/> <title>Vorici Chromatic Calculator</title> <link rel="stylesheet" type="text/css" href="style.css"/> </head> <body> <div class="wrapper"> <div class="core"> <h1>Vorici Chromatic Calculator</h1> <hr> <div class="form"> <table class="formTable"> <tr class="formRow socks"> <td class="rowTitle">Total Sockets</td> <td class="rowField"> <input id="sockets" class="entry white" placeholder="#"></input> </td> <td class="rowField"></td> </tr> <tr class="formRow reqs"> <td class="rowTitle">Requirements</td> <td class="rowField"> <input id="str" class="entry red " placeholder="str"></input> </td> <td class="rowField"> <input id="dex" class="entry green " placeholder="dex"></input> </td> <td class="rowField"> <input id="int" class="entry blue " placeholder="int"></input> </td> <td class="accept"> <button id="calcButton" class="go">Calculate</button> </td> </tr> <tr class="formRow colors"> <td class="rowTitle">Desired Colors</td> <td class="rowField"> <input id="red" class="entry red" placeholder="R"></input> </td> <td class="rowField"> <input id="green" class="entry green" placeholder="G"></input> </td> <td class="rowField"> <input id="blue" class="entry blue" placeholder="B"></input> </td> </tr> </tbody> </table> </div> <hr> <div class="results"> <table class="result sortable" id="result"> <thead> <tr class="prob"> <th>Craft Type</th> <th>Average Cost<br><span class="tablesubtitle">(in chromatics)</span></th> <th>Success Chance</th> <th>Average Attempts<br><span class="tablesubtitle">(mean)</span></th> <th>Cost per Try<br><span class="tablesubtitle">(in chromatics)</span></th> <th>Std. Deviation<br><span class="tablesubtitle">(of attempts)</span></th> </tr> </thead> <tbody id="resultbody"> </tbody> <tbody id="recycle"> </tbody> </table> </div> <div class="etc"> <hr> <br> <span style="font-weight:bold;">Note: Chromatic orbs cannot reroll the same color permutation twice, so the chromatic success chance is always higher than the drop rate.</span> <br>For mono-requirement items, on-color: 0.9 * (R + 10) / (R + 20) <br>For mono-requirement items, off-color: 0.05 + 4.5 / (R + 20) <br>For dual-requirement items, on-color: 0.9 * R1 / (R1 + R2) <br>For dual-requirement items, off-color: 10% flat chance, regardless of requirements <br>The formulas and color chances given are not guaranteed to be right (but I tried!) <br><br><hr><br> Developed by <a href="https://www.edopedia.com/">Edopedia</a> <br><br>Read step-by-step tutorial to create a <a href="https://www.edopedia.com/blog/build-vorici-chromatic-calculator-using-html5-and-javascript/">Vorici Calculator</a> </div> </div> <script src="SortTable.min.js"></script> <script src="vorici_calculator.js"></script> </div></body> </html>
style.css
html { height: 100%; width: 100%; color: #F0F0F0; background: #d294ff; } body { height: 100%; width: 100%; margin: 0; padding: 0; background-repeat: no-repeat; background-attachment: fixed; } h1 { text-align: center; } hr { border-color: #171717; opacity: 0.5; } div.core { width: 61.8%; min-width: 708px; max-width: 1000px; background: #4b0082; padding: 30px; padding-top: 15px; font-family: Arial; font-size: 110%; box-shadow: 3px 5px 20px #030303; border-radius: 5px; } div.form { width: 100%; margin-bottom: 2%; margin-top: 2%; } div.wrapper { padding: 3vh 0; min-width: 800px; display: flex; flex-direction: column; align-items: center; } @media(max-width: 800px) { div.core { width: calc(100% - 60px); margin: 0; } } .entry { background: #c5c5c5; padding: 2px; margin: 2px; color: #000; font-family: Arial; font-size: 110%; width: 60px; text-align: center; border-radius: 3px; border: solid 1px; border-color: #fff; } span.rowTitle { width: 30%; padding-right: 2px; } span.rowEntry { width: 60%; } table.result { width: 100%; } table.formTable { width: 560px; margin-left: auto; margin-right: auto; } th { width: 16.67%; } td.rowTitle { width: 27.5%; text-align: right; } td.rowField { width: 15%; } tr.prob:nth-child(even) { background: #333333; } tr.prob:nth-child(odd) { background: #292929; } tr.reverseStripe:nth-child(odd) { background: #333333; } tr.reverseStripe:nth-child(even) { background: #292929; } tr.best:nth-child(even) { background: #303C30; } tr.best:nth-child(odd) { background: #2A392A; } thead tr.prob:nth-child(odd) { background: #333333; } th { border-left: 1px #222; border-right: 1px #222; padding-left: 10px; padding-right: 10px; padding-top: 5px; padding-bottom: 5px; } td { border-left: 0px #222; border-right: 0px #222; margin-bottom: 0px; margin-top: 0px; padding-left: 10px; padding-right: 10px; padding-top: 5px; padding-bottom: 5px; text-align: center; } .red { background-color: #ffa7a7; border-color: #fff; } .green { background-color: #c3ffc3; border-color: #fff; } .blue { background-color: #c8c8ff; border-color: #fff; } ::-webkit-input-placeholder { opacity: 0.5; } ::-moz-placeholder { opacity: 0.5; } :-ms-input-placeholder { opacity: 0.5; } button { background-color: #555; color: #EEEEEE; font-size: 110%; border: solid 1px #666; border-radius: 3px; padding-left: 12px; padding-right: 12px; padding-top: 5px; padding-bottom: 5px; cursor: pointer; webkit-transition: background-color 300ms ease-out; -moz-transition: background-color 300ms ease-out; transition: background-color 300ms ease-out; } button:hover { background-color: #444; } a:link { color: #d294ff; } a:visited { color: #d294ff; } div.results { margin-bottom: 2.5%; margin-top: 2.5%; } div.etc { width: 100%; color: #e5e5e5; font-size: 80%; text-align: center; position: relative; bottom: 0; } img { padding: 0px; margin: 0px; margin-bottom: -1px; } span.tablesubtitle { font-size: 80%; padding-top: 0px; margin-top: 0px; } .best { color: #EEFFEE; border-left: 2px solid #666666; border-right: 2px solid #666666; } table.sortable thead th { cursor: pointer; webkit-transition: background-color 300ms ease-out; -moz-transition: background-color 300ms ease-out; transition: background-color 300ms ease-out; } table.sortable thead th:hover { background-color: #3F3F3F; } td.long { width: 22.5%; text-align: center; } td.squish { width: 0%; } td.accept { width: 27.5%; } span.highlighted { font-weight: bold; color: #EEEEAA }
vorici_calculator.js
(function (console, $hx_exports) { "use strict"; function $extend(from, fields) { function Inherit() {} Inherit.prototype = from; var proto = new Inherit(); for (var name in fields) proto[name] = fields[name]; if( fields.toString !== Object.prototype.toString ) proto.toString = fields.toString; return proto; } var Colored = function(red,green,blue) { this.red = red; this.green = green; this.blue = blue; }; Colored.__name__ = true; Colored.prototype = { map: function(func) { return new Colored(func(this.red),func(this.green),func(this.blue)); } ,zipMap: function(other,func) { return new Colored(func(this.red,other.red),func(this.green,other.green),func(this.blue,other.blue)); } ,countNonZero: function() { return (this.red > 0?1:0) + (this.green > 0?1:0) + (this.blue > 0?1:0); } ,total: function() { return this.red + this.green + this.blue; } ,toString: function() { return "Red: " + Std.string(this.red) + " | Green: " + Std.string(this.green) + " | Blue: " + Std.string(this.blue); } ,add: function(other) { this.red += other.red; this.green += other.green; this.blue += other.blue; } ,set: function(red,green,blue) { this.red = red; this.green = green; this.blue = blue; } }; var HxOverrides = function() { }; HxOverrides.__name__ = true; HxOverrides.cca = function(s,index) { var x = s.charCodeAt(index); if(x != x) return undefined; return x; }; HxOverrides.substr = function(s,pos,len) { if(pos != null && pos != 0 && len != null && len < 0) return ""; if(len == null) len = s.length; if(pos < 0) { pos = s.length + pos; if(pos < 0) pos = 0; } else if(len < 0) len = s.length + len - pos; return s.substr(pos,len); }; var Main = $hx_exports.Main = function() { }; Main.__name__ = true; Main.main = function() { Main.recipes = []; Main.recipes.push(new Recipe(0,0,0,1,0,"Drop Rate")); Main.recipes.push(new Recipe(0,0,0,1,0,"Chromatic")); Main.recipes.push(new Recipe(1,0,0,4,2)); Main.recipes.push(new Recipe(0,1,0,4,2)); Main.recipes.push(new Recipe(0,0,1,4,2)); Main.recipes.push(new Recipe(2,0,0,25,3)); Main.recipes.push(new Recipe(0,2,0,25,3)); Main.recipes.push(new Recipe(0,0,2,25,3)); Main.recipes.push(new Recipe(0,1,1,15,4)); Main.recipes.push(new Recipe(1,0,1,15,4)); Main.recipes.push(new Recipe(1,1,0,15,4)); Main.recipes.push(new Recipe(3,0,0,120,6)); Main.recipes.push(new Recipe(0,3,0,120,6)); Main.recipes.push(new Recipe(0,0,3,120,6)); Main.recipes.push(new Recipe(2,1,0,100,7)); Main.recipes.push(new Recipe(2,0,1,100,7)); Main.recipes.push(new Recipe(1,2,0,100,7)); Main.recipes.push(new Recipe(0,2,1,100,7)); Main.recipes.push(new Recipe(1,0,2,100,7)); Main.recipes.push(new Recipe(0,1,2,100,7)); Main.sockField = window.document.getElementById("sockets"); Main.strField = window.document.getElementById("str"); Main.dexField = window.document.getElementById("dex"); Main.intField = window.document.getElementById("int"); Main.redField = window.document.getElementById("red"); Main.greenField = window.document.getElementById("green"); Main.blueField = window.document.getElementById("blue"); Main.table = window.document.getElementById("resultbody"); Main.tableWhole = window.document.getElementById("result"); var i = 0; var j; var _g = 0; var _g1 = Main.recipes; while(_g < _g1.length) { var r = _g1[_g]; ++_g; var tr; var _this = window.document; tr = _this.createElement("tr"); var td; j = 6; while(j > 0) { var _this1 = window.document; td = _this1.createElement("td"); if(i < 4) td.innerHTML = "-"; tr.appendChild(td); --j; } if(i >= 4) tr.style.display = "none"; tr.classList.add("prob"); Main.table.appendChild(tr); ++i; } window.document.getElementById("calcButton").onclick = Main.calculate; }; Main.flipTableStripes = function() { var i = 0; var tr = Main.table.firstElementChild; while(tr != null) { ++i; if(tr.firstElementChild.innerHTML != "") tr = null; else tr = tr.nextElementSibling; } tr = Main.table.firstElementChild; while(tr != null) { tr.classList.toggle("reverseStripe",i % 2 == 0); tr = tr.nextElementSibling; } }; Main.updateTable = function(probs) { var row = Main.table.firstElementChild; var mindex = 0; var min = 0; var i = 0; var j = 0; var _g = 0; while(_g < probs.length) { var p = probs[_g]; ++_g; if(p.favg > 0 && (min == 0 || min > p.favg)) { mindex = i; min = p.favg; } ++i; } var _g1 = 0; while(_g1 < probs.length) { var p1 = probs[_g1]; ++_g1; i = 0; var td = row.firstElementChild; while(td != null) { td.innerHTML = p1.get(i); td = td.nextElementSibling; ++i; } row.style.display = "table-row"; row.classList.toggle("best",mindex == j); row.classList.remove("reverseStripe"); row = row.nextElementSibling; ++j; } while(row != null) { var td1 = row.firstElementChild; while(td1 != null) { td1.innerHTML = ""; td1 = td1.nextElementSibling; } row.style.display = "none"; row.classList.remove("best"); row = row.nextElementSibling; } var th = Main.tableWhole.tHead.firstElementChild.firstElementChild; i = 0; while(th != null) { var s = ""; switch(i) { case 0: s = "Craft Type"; break; case 1: s = "Average Cost<br/><span class=\"tablesubtitle\">(in chromatics)</span>"; break; case 2: s = "Success Chance"; break; case 3: s = "Average Attempts<br/><span class=\"tablesubtitle\">(mean)</span>"; break; case 4: s = "Cost per Try<br/><span class=\"tablesubtitle\">(in chromatics)</span>"; break; case 5: s = "Std. Deviation<br/><span class=\"tablesubtitle\">(of attempts)</span>"; break; } th.classList.remove("SortTable_sorted"); th.classList.remove("SortTable_sorted_reverse"); th.innerHTML = s; th = th.nextElementSibling; ++i; } SortTable.makeSortable(Main.tableWhole); SortTable.makeSortable(Main.tableWhole); }; Main.calculate = function(d) { var probs = []; var error = false; var socks = Std.parseInt(Main.sockField.value); var str = Std.parseInt(Main.strField.value); var dex = Std.parseInt(Main.dexField.value); var $int = Std.parseInt(Main.intField.value); var red = Std.parseInt(Main.redField.value); var green = Std.parseInt(Main.greenField.value); var blue = Std.parseInt(Main.blueField.value); if(str == null) str = 0; if(dex == null) dex = 0; if($int == null) $int = 0; if(socks == null) { socks = red + blue + green; Main.sockField.value = "" + socks; } if(socks <= 0 || socks > 6) { error = true; probs.push(new Probability("Error:","Invalid","number","of","sockets.",":(")); } if(str < 0 || dex < 0 || $int < 0) { error = true; probs.push(new Probability("Error:","Invalid","item","stat","requirements.",":(")); } if(str == 0 && dex == 0 && $int == 0) { error = true; probs.push(new Probability("Error:","Please","fill in","stat","requirements.",":(")); } if(red < 0 || green < 0 || blue < 0 || red + blue + green == 0 || red > 6 || green > 6 || blue > 6 || red + blue + green > socks) { error = true; probs.push(new Probability("Error:","Invalid","desired","socket","colors.",":(")); } if(!error) { var requirements = new Colored(str,dex,$int); var desiredSockets = new Colored(red,green,blue); probs = Main.getProbabilities(requirements,desiredSockets,socks); } Main.updateTable(probs); }; Main.getColorChances = function(requirements) { var X = 5; var C = 5; var maxOnColorChance = 0.9; var totalRequirements = requirements.total(); var numberOfRequirements = requirements.countNonZero(); var requirementToChance = null; switch(numberOfRequirements) { case 1: requirementToChance = function(requirement) { if(requirement > 0) return maxOnColorChance * (X + C + requirement) / (totalRequirements + 3 * X + C); else return (1 - maxOnColorChance) / 2 + maxOnColorChance * (X / (totalRequirements + 3 * X + C)); }; break; case 2: requirementToChance = function(requirement1) { if(requirement1 > 0) return maxOnColorChance * requirement1 / totalRequirements; else return 1 - maxOnColorChance; }; break; case 3: requirementToChance = function(requirement2) { return requirement2 / totalRequirements; }; break; } return requirements.map(requirementToChance); }; Main.getProbabilities = function(requirements,desired,totalSockets) { var probs = []; var colorChances = Main.getColorChances(requirements); Main.simulateLotsOfChromatics(colorChances,totalSockets); var _g = 0; var _g1 = Main.recipes; while(_g < _g1.length) { var recipe = _g1[_g]; ++_g; if(recipe.red <= desired.red && recipe.green <= desired.green && recipe.blue <= desired.blue) { var unvoricifiedDesires = new Colored(desired.red - recipe.red,desired.green - recipe.green,desired.blue - recipe.blue); var howManySocketsDoWeNotCareAbout = totalSockets - desired.total(); var chance = Main.multinomial(colorChances,unvoricifiedDesires,howManySocketsDoWeNotCareAbout); if(recipe.description == "Chromatic") { var chanceForChromaticCollision = Main.calcChromaticBonus(colorChances,desired,totalSockets); chance /= 1 - chanceForChromaticCollision; } probs.push(new Probability(recipe.description,recipe.description == "Drop Rate"?"-":Utils.floatToPrecisionString(recipe.cost / chance,1,null),Utils.floatToPrecisionString(chance * 100,5,false) + "%",Utils.floatToPrecisionString(1 / chance,1,null),recipe.description == "Drop Rate"?"-":recipe.cost == null?"null":"" + recipe.cost,Utils.floatToPrecisionString(Math.sqrt(Utils.clamp(1 - chance,0,1) / (chance * chance)),2,null),recipe.cost / chance)); } } return probs; }; Main.simulateLotsOfChromatics = function(colorChances,totalSockets) { var lastSockets = ""; var sockets = new Colored(0,0,0); var total = new Colored(0,0,0); var i = 0; while(i < 100000) { var j = 0; var currentSockets = ""; sockets.set(0,0,0); while(j < totalSockets) { var r = Math.random(); if(r < colorChances.red) { currentSockets += "R"; sockets.red++; } else if(r < colorChances.green + colorChances.red) { currentSockets += "G"; sockets.green++; } else { currentSockets += "B"; sockets.blue++; } j++; } if(currentSockets == lastSockets) continue; total.add(sockets); lastSockets = currentSockets; i++; } console.log(total.toString()); }; Main.multinomial = function(colorChances,desired,free,pos) { if(pos == null) pos = 1; if(free > 0) return (pos <= 1?Main.multinomial(colorChances,new Colored(desired.red + 1,desired.green,desired.blue),free - 1,1):0) + (pos <= 2?Main.multinomial(colorChances,new Colored(desired.red,desired.green + 1,desired.blue),free - 1,2):0) + Main.multinomial(colorChances,new Colored(desired.red,desired.green,desired.blue + 1),free - 1,3); else return Utils.factorial(desired.total()) / (Utils.factorial(desired.red) * Utils.factorial(desired.green) * Utils.factorial(desired.blue)) * Math.pow(colorChances.red,desired.red) * Math.pow(colorChances.green,desired.green) * Math.pow(colorChances.blue,desired.blue); }; Main.calcChromaticBonus = function(colorChances,desired,free,rolled,pos) { if(pos == null) pos = 1; if(rolled == null) rolled = new Colored(0,0,0); if(rolled.red >= desired.red && rolled.green >= desired.green && rolled.blue >= desired.blue) return 0; else if(free > 0) return (pos <= 1?Main.calcChromaticBonus(colorChances,desired,free - 1,new Colored(rolled.red + 1,rolled.green,rolled.blue),1):0) + (pos <= 2?Main.calcChromaticBonus(colorChances,desired,free - 1,new Colored(rolled.red,rolled.green + 1,rolled.blue),2):0) + Main.calcChromaticBonus(colorChances,desired,free - 1,new Colored(rolled.red,rolled.green,rolled.blue + 1),3); else return Utils.factorial(rolled.total()) / (Utils.factorial(rolled.red) * Utils.factorial(rolled.green) * Utils.factorial(rolled.blue)) * Math.pow(colorChances.red,rolled.red * 2) * Math.pow(colorChances.green,rolled.green * 2) * Math.pow(colorChances.blue,rolled.blue * 2); }; Math.__name__ = true; var Probability = function(recipeName,avgCost,chance,avgTries,recipeCost,stdDev,favg) { if(favg == null) favg = 0; this.recipeName = recipeName; this.chance = chance; this.avgTries = avgTries; this.recipeCost = recipeCost; this.avgCost = avgCost; this.stdDev = stdDev; this.favg = favg; }; Probability.__name__ = true; Probability.prototype = { get: function(part) { switch(part) { case 0: return this.recipeName; case 1: return "<span class=\"highlighted\">" + this.avgCost + "</b>"; case 2: return this.chance; case 3: return this.avgTries; case 4: return this.recipeCost; case 5: return this.stdDev; default: return "N/A"; } } }; var Recipe = function(r,g,b,c,l,d) { Colored.call(this,r,g,b); this.cost = c; this.level = l; if(d == null) this.description = "Vorici " + (r > 0?r + "R":"") + (g > 0?g + "G":"") + (b > 0?b + "B":""); else this.description = d; }; Recipe.__name__ = true; Recipe.__super__ = Colored; Recipe.prototype = $extend(Colored.prototype,{ }); var Std = function() { }; Std.__name__ = true; Std.string = function(s) { return js_Boot.__string_rec(s,""); }; Std.parseInt = function(x) { var v = parseInt(x,10); if(v == 0 && (HxOverrides.cca(x,1) == 120 || HxOverrides.cca(x,1) == 88)) v = parseInt(x); if(isNaN(v)) return null; return v; }; var StringBuf = function() { this.b = ""; }; StringBuf.__name__ = true; StringBuf.prototype = { addSub: function(s,pos,len) { if(len == null) this.b += HxOverrides.substr(s,pos,null); else this.b += HxOverrides.substr(s,pos,len); } }; var StringTools = function() { }; StringTools.__name__ = true; StringTools.replace = function(s,sub,by) { return s.split(sub).join(by); }; var Utils = function() { }; Utils.__name__ = true; Utils.wrapAngle = function(x) { return Utils.wrap2(x,-Math.PI,Math.PI); }; Utils.wrap = function(v,limit) { while(v < 0) v += limit; while(v > limit) v -= limit; return v; }; Utils.wrap2 = function(v,lowerLimit,upperLimit) { v = Utils.wrap(v - lowerLimit,upperLimit); return v + lowerLimit; }; Utils.abs = function(a) { if(a > 0) return a; else return -a; }; Utils.max = function(a,b) { if(a > b) return a; else return b; }; Utils.min = function(a,b) { if(a < b) return a; else return b; }; Utils.clamp = function(a,min,max) { if(a < min) return min; else if(a > max) return max; else return a; }; Utils.iabs = function(a) { if(a > 0) return a; else return -a; }; Utils.imax = function(a,b) { if(a > b) return a; else return b; }; Utils.imin = function(a,b) { if(a < b) return a; else return b; }; Utils.iclamp = function(a,min,max) { if(a < min) return min; else if(a > max) return max; else return a; }; Utils.stripSpace = function(s) { var output; output = StringTools.replace(s," ",""); output = StringTools.replace(s,"\t",""); output = StringTools.replace(s,"\n",""); return output; }; Utils.lineCollide = function(l1,r1,l2,r2) { return l1 <= r2 && r1 >= l2; }; Utils.rectContains = function(leftX,topY,width,height,x,y) { return x >= leftX && x <= leftX + width && y <= topY + height && y >= topY; }; Utils.rectGridIndex = function(leftX,topY,widthInTiles,heightInTiles,tileWidth,tileHeight,x,y) { if(x >= leftX && x <= leftX + (widthInTiles * tileWidth - 1) && y <= topY + (heightInTiles * tileHeight - 1) && y >= topY) return ((x - leftX) / tileWidth | 0) + widthInTiles * ((y - topY) / tileWidth | 0); else return -1; }; Utils.triContains = function(x1,y1,x2,y2,x3,y3,x,y) { var lx; var ly; var mx; var my; var rx; var ry; var crossY1; var crossY2; var midident; if(x1 <= x2 && x1 <= x3) { lx = x1; ly = y1; midident = 1; } else if(x2 <= x1 && x2 <= x3) { lx = x2; ly = y2; midident = 2; } else { lx = x3; ly = y3; midident = 3; } if(x1 > x2 && x1 > x3) { rx = x1; ry = y1; midident += 1; } else if(x2 > x1 && x2 > x3) { rx = x2; ry = y2; midident += 2; } else { rx = x3; ry = y3; midident += 3; } if(midident == 5) { mx = x1; my = y1; } else if(midident == 4) { mx = x2; my = y2; } else { mx = x3; my = y3; } if(x >= lx && x <= rx) { if(x < mx) { crossY1 = (x - lx) / (mx - lx) * (my - ly) + ly | 0; crossY2 = (x - lx) / (rx - lx) * (ry - ly) + ly | 0; if(crossY1 <= crossY2) return y >= crossY1 && y <= crossY2; else return y <= crossY1 && y >= crossY2; } else { if(rx == mx) { if(rx == lx) { crossY1 = Utils.imin(ly,my < ry?my:ry); crossY2 = Utils.imax(ly,my > ry?my:ry); } else { crossY1 = my; crossY2 = ry; } } else { crossY1 = (rx - x) / (rx - mx) * (my - ry) + ly | 0; crossY2 = (rx - x) / (rx - lx) * (ly - ry) + ly | 0; } if(crossY1 <= crossY2) return y >= crossY1 && y <= crossY2; else return y <= crossY1 && y >= crossY2; } } return false; }; Utils.properTriContains = function(lx,ly,mx,my,rx,ry,x,y) { var crossY1; var crossY2; if(x >= lx && x <= rx) { if(x < mx) { crossY1 = (x - lx) / (mx - lx) * (my - ly) + ly | 0; crossY2 = (x - lx) / (rx - lx) * (ry - ly) + ly | 0; if(crossY1 <= crossY2) return y >= crossY1 && y <= crossY2; else return y <= crossY1 && y >= crossY2; } else { crossY1 = (rx - x) / (rx - mx) * (my - ry) + ry | 0; crossY2 = (rx - x) / (rx - lx) * (ly - ry) + ry | 0; if(crossY1 <= crossY2) return y >= crossY1 && y <= crossY2; else return y <= crossY1 && y >= crossY2; } } return false; }; Utils.circleContains = function(centerX,centerY,radius,x,y) { return (x - centerX) * (x - centerX) + (y - centerY) * (y - centerY) <= radius * radius; }; Utils.intAsFixedString = function(val,digits) { var limit = 1; var i = digits; while(i > 0) { --i; limit *= 10; } var string = Std.string(Utils.imin(limit - 1,val)); digits -= string.length; while(digits > 0) { --digits; string = "0" + string; } return string; }; Utils.intAsFixedHTML = function(val,digits,numColor,zeroColor) { if(zeroColor == null) zeroColor = "444444"; if(numColor == null) numColor = "161616"; var limit = 1; var i = digits; while(i > 0) { --i; limit *= 10; } var string = Std.string(Utils.imin(limit - 1,val)); digits -= string.length; if(digits > 0) { string = "</font>" + string + "</font>"; while(digits > 0) { --digits; string = "0" + string; } string = "<font color=\"#" + numColor + "\">" + "<font color=\"#" + zeroColor + "\">" + string; } return string; }; Utils.floatAsIntString = function(val) { return (val == null?"null":"" + val).split(".")[0]; }; Utils.floatAsFixedHTML = function(val,digits,numColor,zeroColor) { if(zeroColor == null) zeroColor = "444444"; if(numColor == null) numColor = "161616"; var limit = 1; var i = digits; while(i > 0) { --i; limit *= 10; } var string = Std.string(Utils.min(limit - 1,val)).split(".")[0]; digits -= string.length; if(digits > 0) { string = "</font>" + string + "</font>"; while(digits > 0) { --digits; string = "0" + string; } string = "<font color=\"#" + numColor + "\">" + "<font color=\"#" + zeroColor + "\">" + string; } return string; }; Utils.greaterQuadratic = function(a,b,c) { var retval = 0; if(b * b - 4 * a * c >= 0) retval = (-b + Math.sqrt(b * b - 4 * a * c)) / (2 * a); return retval; }; Utils.lesserQuadratic = function(a,b,c) { var retval = 0; if(b * b - 4 * a * c >= 0) retval = (-b - Math.sqrt(b * b - 4 * a * c)) / (2 * a); return retval; }; Utils.sign = function(a) { if(a > 0) return 1; else if(a < 0) return -1; else return 0; }; Utils.isign = function(a) { if(a > 0) return 1; else if(a < 0) return -1; else return 0; }; Utils.wordWrap = function(text,charsPerLine) { if(charsPerLine <= 0) return ""; else { var i = 0; var lastSpace = -1; var lastLine = 0; var currentLine = 0; var len = text.length; var outs = new StringBuf(); while(i < len) { if(HxOverrides.cca(text,i) == 32) lastSpace = i; else if(HxOverrides.cca(text,i) == 10) { outs.addSub(text,lastLine,i - lastLine + 1); currentLine = 0; lastLine = i + 1; } if(currentLine >= charsPerLine) { if(lastSpace == -1) { if(charsPerLine == null) outs.b += HxOverrides.substr(text,lastLine,null); else outs.b += HxOverrides.substr(text,lastLine,charsPerLine); outs.b += "\n"; lastLine = lastLine + charsPerLine; } else { outs.addSub(text,lastLine,lastSpace - lastLine); outs.b += "\n"; lastLine = lastSpace + 1; } currentLine = i - lastLine; lastSpace = -1; } ++currentLine; ++i; } if(lastLine < len - 1) if(charsPerLine == null) outs.b += HxOverrides.substr(text,lastLine,null); else outs.b += HxOverrides.substr(text,lastLine,charsPerLine); return outs.b; } }; Utils.intNoOverflow = function(f) { if(f >= 2147483647.0) return 2147483647; else if(f <= -2147483647.) return -1; else return f | 0; }; Utils.floatToPrecisionString = function(f,precision,commas) { if(commas == null) commas = true; if(precision == null) precision = 5; var prefix = ""; if(f < 0) { f *= -1; prefix = "-"; } f = f * Math.pow(10,precision); var i = Math.round(f); if(i == 0 && f > 0.00000000001) { i = 1; if(prefix == "-") prefix = ">-"; else prefix = "<"; } var s; if(i == null) s = "null"; else s = "" + i; if(s.length <= precision) s = Utils.intAsFixedString(i,precision + 1); var result = ""; var j = s.length - precision; if(commas) { while(j > 3) { j -= 3; result = "," + HxOverrides.substr(s,j,3) + result; } result = s.substring(0,j) + result + "." + HxOverrides.substr(s,s.length - precision,null); } else result = HxOverrides.substr(s,0,s.length - precision) + "." + HxOverrides.substr(s,s.length - precision,null); return prefix + result; }; Utils.floatToPercent = function(f,precision) { if(precision == null) precision = 5; return Utils.floatToPrecisionString(f * 100,precision,false) + "%"; }; Utils.mod = function(a,b) { return a - (a / b | 0) * b; }; Utils.log2 = function(v) { var r = 0; while(v >= 31) { v >>= 4; r += 4; } while(v > 1) { v >>= 1; ++r; } return r; }; Utils.factorial = function(x) { var sign = 1; var r = 1; if(x < 0) { sign = -1; x *= -1; } while(x > 1) { r *= x; --x; } return r * sign; }; var js_Boot = function() { }; js_Boot.__name__ = true; js_Boot.__string_rec = function(o,s) { if(o == null) return "null"; if(s.length >= 5) return "<...>"; var t = typeof(o); if(t == "function" && (o.__name__ || o.__ename__)) t = "object"; switch(t) { case "object": if(o instanceof Array) { if(o.__enum__) { if(o.length == 2) return o[0]; var str2 = o[0] + "("; s += "\t"; var _g1 = 2; var _g = o.length; while(_g1 < _g) { var i1 = _g1++; if(i1 != 2) str2 += "," + js_Boot.__string_rec(o[i1],s); else str2 += js_Boot.__string_rec(o[i1],s); } return str2 + ")"; } var l = o.length; var i; var str1 = "["; s += "\t"; var _g2 = 0; while(_g2 < l) { var i2 = _g2++; str1 += (i2 > 0?",":"") + js_Boot.__string_rec(o[i2],s); } str1 += "]"; return str1; } var tostr; try { tostr = o.toString; } catch( e ) { return "???"; } if(tostr != null && tostr != Object.toString && typeof(tostr) == "function") { var s2 = o.toString(); if(s2 != "[object Object]") return s2; } var k = null; var str = "{\n"; s += "\t"; var hasp = o.hasOwnProperty != null; for( var k in o ) { if(hasp && !o.hasOwnProperty(k)) { continue; } if(k == "prototype" || k == "__class__" || k == "__super__" || k == "__interfaces__" || k == "__properties__") { continue; } if(str.length != 2) str += ", \n"; str += s + k + " : " + js_Boot.__string_rec(o[k],s); } s = s.substring(1); str += "\n" + s + "}"; return str; case "function": return "<function>"; case "string": return o; default: return String(o); } }; String.__name__ = true; Array.__name__ = true; Utils.TWOPI = 6.28318530717958647693; Main.main(); })(typeof console != "undefined" ? console : {log:function(){}}, typeof window != "undefined" ? window : exports);
SortTable.min.js
/* SortTable version 2 7th April 2007 Stuart Langridge, http://www.kryogenix.org/code/browser/SortTable/ Thanks to many, many people for contributions and suggestions. Licenced as X11: http://www.kryogenix.org/code/browser/licence.html This basically means: do what you want with it. */ function dean_addEvent(e,t,n){if(e.addEventListener){e.addEventListener(t,n,false)}else{if(!n.$$guid)n.$$guid=dean_addEvent.guid++;if(!e.events)e.events={};var r=e.events[t];if(!r){r=e.events[t]={};if(e["on"+t]){r[0]=e["on"+t]}}r[n.$$guid]=n;e["on"+t]=handleEvent}}function removeEvent(e,t,n){if(e.removeEventListener){e.removeEventListener(t,n,false)}else{if(e.events&&e.events[t]){delete e.events[t][n.$$guid]}}}function handleEvent(e){var t=true;e=e||fixEvent(((this.ownerDocument||this.document||this).parentWindow||window).event);var n=this.events[e.type];for(var r in n){this.$$handleEvent=n[r];if(this.$$handleEvent(e)===false){t=false}}return t}function fixEvent(e){e.preventDefault=fixEvent.preventDefault;e.stopPropagation=fixEvent.stopPropagation;return e}var stIsIE=false;SortTable={init:function(){if(arguments.callee.done)return;arguments.callee.done=true;if(_timer)clearInterval(_timer);if(!document.createElement||!document.getElementsByTagName)return;SortTable.DATE_RE=/^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/;forEach(document.getElementsByTagName("table"),function(e){if(e.className.search(/\bsortable\b/)!=-1){SortTable.makeSortable(e)}})},makeSortable:function(e){if(e.getElementsByTagName("thead").length==0){the=document.createElement("thead");the.appendChild(e.rows[0]);e.insertBefore(the,e.firstChild)}if(e.tHead==null)e.tHead=e.getElementsByTagName("thead")[0];if(e.tHead.rows.length!=1)return;sortbottomrows=[];for(var t=0;t<e.rows.length;t++){if(e.rows[t].className.search(/\bsortbottom\b/)!=-1){sortbottomrows[sortbottomrows.length]=e.rows[t]}}if(sortbottomrows){if(e.tFoot==null){tfo=document.createElement("tfoot");e.appendChild(tfo)}for(var t=0;t<sortbottomrows.length;t++){tfo.appendChild(sortbottomrows[t])}delete sortbottomrows}headrow=e.tHead.rows[0].cells;for(var t=0;t<headrow.length;t++){if(!headrow[t].className.match(/\bSortTable_nosort\b/)){mtch=headrow[t].className.match(/\bSortTable_([a-z0-9]+)\b/);if(mtch){override=mtch[1]}if(mtch&&typeof SortTable["sort_"+override]=="function"){headrow[t].SortTable_sortfunction=SortTable["sort_"+override]}else{headrow[t].SortTable_sortfunction=SortTable.guessType(e,t)}headrow[t].SortTable_columnindex=t;headrow[t].SortTable_tbody=e.tBodies[0];dean_addEvent(headrow[t],"click",SortTable.innerSortFunction=function(e){if(this.className.search(/\bSortTable_sorted\b/)!=-1){SortTable.reverse(this.SortTable_tbody);this.className=this.className.replace("SortTable_sorted","SortTable_sorted_reverse");this.removeChild(document.getElementById("SortTable_sortfwdind"));sortrevind=document.createElement("span");sortrevind.id="SortTable_sortrevind";sortrevind.innerHTML=stIsIE?' <font face="webdings">5</font>':" ▴";this.appendChild(sortrevind);Main.flipTableStripes();return}if(this.className.search(/\bSortTable_sorted_reverse\b/)!=-1){SortTable.reverse(this.SortTable_tbody);this.className=this.className.replace("SortTable_sorted_reverse","SortTable_sorted");this.removeChild(document.getElementById("SortTable_sortrevind"));sortfwdind=document.createElement("span");sortfwdind.id="SortTable_sortfwdind";sortfwdind.innerHTML=stIsIE?' <font face="webdings">6</font>':" ▾";this.appendChild(sortfwdind);Main.flipTableStripes();return}theadrow=this.parentNode;forEach(theadrow.childNodes,function(e){if(e.nodeType==1){e.className=e.className.replace("SortTable_sorted_reverse","");e.className=e.className.replace("SortTable_sorted","")}});sortfwdind=document.getElementById("SortTable_sortfwdind");if(sortfwdind){sortfwdind.parentNode.removeChild(sortfwdind)}sortrevind=document.getElementById("SortTable_sortrevind");if(sortrevind){sortrevind.parentNode.removeChild(sortrevind)}this.className+=" SortTable_sorted";sortfwdind=document.createElement("span");sortfwdind.id="SortTable_sortfwdind";sortfwdind.innerHTML=stIsIE?' <font face="webdings">6</font>':" ▾";this.appendChild(sortfwdind);row_array=[];col=this.SortTable_columnindex;rows=this.SortTable_tbody.rows;for(var t=0;t<rows.length;t++){row_array[row_array.length]=[SortTable.getInnerText(rows[t].cells[col]),rows[t]]}row_array.sort(this.SortTable_sortfunction);tb=this.SortTable_tbody;for(var t=0;t<row_array.length;t++){tb.appendChild(row_array[t][1])}delete row_array})}}},guessType:function(e,t){sortfn=SortTable.sort_alpha;for(var n=0;n<e.tBodies[0].rows.length;n++){text=SortTable.getInnerText(e.tBodies[0].rows[n].cells[t]);if(text!=""){if(text.match(/^-?[£$¤]?[\d,.]+%?$/)){return SortTable.sort_numeric}possdate=text.match(SortTable.DATE_RE);if(possdate){first=parseInt(possdate[1]);second=parseInt(possdate[2]);if(first>12){return SortTable.sort_ddmm}else if(second>12){return SortTable.sort_mmdd}else{sortfn=SortTable.sort_ddmm}}}}return sortfn},getInnerText:function(e){if(!e)return"";hasInputs=typeof e.getElementsByTagName=="function"&&e.getElementsByTagName("input").length;if(e.getAttribute("SortTable_customkey")!=null){return e.getAttribute("SortTable_customkey")}else if(typeof e.textContent!="undefined"&&!hasInputs){return e.textContent.replace(/^\s+|\s+$/g,"")}else if(typeof e.innerText!="undefined"&&!hasInputs){return e.innerText.replace(/^\s+|\s+$/g,"")}else if(typeof e.text!="undefined"&&!hasInputs){return e.text.replace(/^\s+|\s+$/g,"")}else{switch(e.nodeType){case 3:if(e.nodeName.toLowerCase()=="input"){return e.value.replace(/^\s+|\s+$/g,"")};case 4:return e.nodeValue.replace(/^\s+|\s+$/g,"");break;case 1:case 11:var t="";for(var n=0;n<e.childNodes.length;n++){t+=SortTable.getInnerText(e.childNodes[n])}return t.replace(/^\s+|\s+$/g,"");break;default:return""}}},reverse:function(e){newrows=[];for(var t=0;t<e.rows.length;t++){newrows[newrows.length]=e.rows[t]}for(var t=newrows.length-1;t>=0;t--){e.appendChild(newrows[t])}delete newrows},sort_numeric:function(e,t){if(e[0]=="")return 1;else if(t[0]=="")return-1;if(e[0]=="-")return 1;else if(t[0]=="-")return-1;aa=parseFloat(e[0].replace(/[^0-9.-]/g,""));if(isNaN(aa))aa=0;bb=parseFloat(t[0].replace(/[^0-9.-]/g,""));if(isNaN(bb))bb=0;return aa-bb},sort_alpha:function(e,t){if(e[0]==t[0])return 0;if(e[0]<t[0])return-1;return 1},sort_ddmm:function(e,t){mtch=e[0].match(SortTable.DATE_RE);y=mtch[3];m=mtch[2];d=mtch[1];if(m.length==1)m="0"+m;if(d.length==1)d="0"+d;dt1=y+m+d;mtch=t[0].match(SortTable.DATE_RE);y=mtch[3];m=mtch[2];d=mtch[1];if(m.length==1)m="0"+m;if(d.length==1)d="0"+d;dt2=y+m+d;if(dt1==dt2)return 0;if(dt1<dt2)return-1;return 1},sort_mmdd:function(e,t){mtch=e[0].match(SortTable.DATE_RE);y=mtch[3];d=mtch[2];m=mtch[1];if(m.length==1)m="0"+m;if(d.length==1)d="0"+d;dt1=y+m+d;mtch=t[0].match(SortTable.DATE_RE);y=mtch[3];d=mtch[2];m=mtch[1];if(m.length==1)m="0"+m;if(d.length==1)d="0"+d;dt2=y+m+d;if(dt1==dt2)return 0;if(dt1<dt2)return-1;return 1},shaker_sort:function(e,t){var n=0;var r=e.length-1;var i=true;while(i){i=false;for(var s=n;s<r;++s){if(t(e[s],e[s+1])>0){var o=e[s];e[s]=e[s+1];e[s+1]=o;i=true}}r--;if(!i)break;for(var s=r;s>n;--s){if(t(e[s],e[s-1])<0){var o=e[s];e[s]=e[s-1];e[s-1]=o;i=true}}n++}}};if(document.addEventListener){document.addEventListener("DOMContentLoaded",SortTable.init,false)}if(/WebKit/i.test(navigator.userAgent)){var _timer=setInterval(function(){if(/loaded|complete/.test(document.readyState)){SortTable.init()}},10)}window.onload=SortTable.init;dean_addEvent.guid=1;fixEvent.preventDefault=function(){this.returnValue=false};fixEvent.stopPropagation=function(){this.cancelBubble=true};if(!Array.forEach){Array.forEach=function(e,t,n){for(var r=0;r<e.length;r++){t.call(n,e[r],r,e)}}}Function.prototype.forEach=function(e,t,n){for(var r in e){if(typeof this.prototype[r]=="undefined"){t.call(n,e[r],r,e)}}};String.forEach=function(e,t,n){Array.forEach(e.split(""),function(r,i){t.call(n,r,i,e)})};var forEach=function(e,t,n){if(e){var r=Object;if(e instanceof Function){r=Function}else if(e.forEach instanceof Function){e.forEach(t,n);return}else if(typeof e=="string"){r=String}else if(typeof e.length=="number"){r=Array}r.forEach(e,t,n)}}