Gilgamech Technologies

From the sublime to the ridiculous.



01/31/2022 - Mike Wazowski cipher.

//Myszkowski cipher uses a key to rearrange a message. It only works if the message length is a multiple of the key length. This example message is 90 characters without spaces, which is exactly 15 times longer than the 6 character key.

//Key:
//Input:
//Output:
//Revert:

It took about 4 days to figure out the encipherment, and about 8 days to figure out the revert function. Though I was ill...

First, the easy part: Very carefully making a mess of things.

Encipherment starts with uppercasing the key and message, and removing spaces. Also create the Out variable as a function-level for later.

function getMyszkowski(message,key) {
    message = message.toUpperCase().replace(/ /g,"");
    key = key.toUpperCase();
    var out = "";

//This cipher works by looping it around the key length before rearranging the key alphabetically. It's a nest of loops. So we start by breaking the message into key-sized chunks with the outermost loop, cycle through the caps alphabet in order, then cycle throgh the message's actual letters within that. The outermost loop creates the Current Message Chunk from which the innermost loop extracts the current letter.

    for (messageChunkIndex = 0;messageChunkIndex<message.length;messageChunkIndex+=key.length) {
      var currentMessageChunk = message.substring(messageChunkIndex,messageChunkIndex+key.length);

      for (currentAlphaCode = 65;currentAlphaCode<91;currentAlphaCode++)
        var currentAlpha = String.fromCharCode(currentAlphaCode);

        for (messageIndex=0;messageIndex<currentMessageChunk.length;messageIndex++) {
          var msgLetter = currentMessageChunk.substring(messageIndex,messageIndex+1);

//The innermost loop checks if the key at the current location matches the middle loop's alphabet letter - if so, the message letter is written to the Out variable. Then close all the loops, return the Out variable, and close the function.

          var keyLetter = key.substring(messageIndex%key.length,messageIndex%key.length+1);
          if (keyLetter == currentAlpha){

            out += msgLetter;
          }// end if keyLetter
        }// end for messageIndex
      }// end for currentAlphaCode
    }// end for messageChunkIndex
    return out;
}; //end getMyszkowski

Now the hard part: Getting back to where we were in the first place.

Decipherment begins with copypasta of the first section:

function revertMyszkowski(message,key) {
    message = message.toUpperCase().replace(/ /g,"");
    key = key.toUpperCase();
    var out = "";

//Set up the Current Message Chunk in the outer loop, just as before. Set it up as an array, and the key as keySplit also an array.

    for (messageChunkIndex = 0;messageChunkIndex<message.length;messageChunkIndex+=key.length) {
      var currentMessageChunk = message.substring(messageChunkIndex,messageChunkIndex+key.length);
      var currentMessageChunk = currentMessageChunk.split('');
      var keySplit = key.split('');

//Cycle through the current key in order - this replaces the alphabetic loop from the first function, as here the key starts alphabetized and is returned to key-order - and the message with it. The key is alphabetized into an array as keySorted.

      for (KeyIndex = 0;KeyIndex<keySplit.length;KeyIndex++) {
        var keySorted = key.split('').sort();

//In the innermost loop, cycle through the the letters of each message chunk as an array. The individual letters are set up from their respective indices. The Key Sorted Index is also set up, which is the location of the current key letter within the alphabetized array. Keeping the key columns in order is essential to making this decipherment work correctly.

        for (messageIndex=0;messageIndex<keySplit.length;messageIndex++) {
          var keySortedLetter = keySorted[messageIndex];
          var keySplitLetter = keySplit[KeyIndex];
          var keySortedIndex = keySorted.indexOf(keySplitLetter);
          var msgLetter = currentMessageChunk[keySortedIndex];

//If the sorted and split keys have the same letter, add it to the Out variable. And remove the current letter from each of the arrays, and reset to the start of the message chunk loop again.

          if (keySortedLetter == keySplitLetter){
            out += msgLetter;

            keySplit.splice(KeyIndex,1);
            keySorted.splice(keySortedIndex,1);
            currentMessageChunk.splice(keySortedIndex,1);

            messageIndex=0;
          }// end if keyLetter

//If there's only one letter left in the chunk, add that to the Out variable too - this is a bugfix because it kept leaving off the last letter in each message chunk. Close all the loops and return the Out variable before closing the function.

          if (currentMessageChunk.length == 1) {
            out += currentMessageChunk[0];
          }
        }// end for messageIndex
      }// end for keySplitLetter
    }// end for messageChunkIndex
    return out;
}; //end revertMyszkowski

//As with Porta, this could be extended to better handle spaces and other punctuation.



01/28/2022 - NASA conspiracy!

Looking at the ISS stream showed this weird image:

Checking again a few hours later reveals the truth:

I've been watching too many UFO videos lately. The ISS was viewing Earth's dark side, but wasn't in Earth's shadow - hence the dark side of the Earth was washed out by the much brighter parts of the ISS. Occam's razor suggests amateur photography mistakes are much more likely than interstellar conspiracy.



01/26/2022 - Powershell default color.

Windows 11 is available for prerelease install. Some of the features aren't quite complete - like hiding favorite files from the start bar.

And some cheese definitely gets moved. Powershell changes from the pretty dark blue from before, to a very dark grey. The grey is nearly black, but makes sense when you convert to hex:

It's subtle, like a joke in HTML color language. Technically the new colors are slightly less visible, as (204 - 12) * 3 = 576, while 254 + 219 + 169 = 642. As I discussed back in August, the "minimum contrast" for visibility is a total difference of 500, which the new scheme meets. But it has 66 less total contrast than the classic Blue scheme. Given that this color space is 765 units large, the new scheme's difference of 66 color points makes it 8.6% less visible than the classic Blue. Something to keep in mind for low visibility and low brightness situations.

If all you want is the normal Powershell colors again, just right click the top bar > Properites > Colors tab, and enter these values for the background:

Then select the Screen Text radio button, and update the text color:

Then click "OK" and you're back in blue. Now you'll just need something borrowed.



01/23/2022 - Steam Heat.

Nuclear power is a hot topic - indeed a charged one, electric with opinions. All of them emotional - don't we "trust" the subatomic particles? Or are we some kind of uneducated yeller-bellied chicken-herder?

What is Nuclear Fission Power?

Nuclear fission power is the energy-as-velocity carried by subatomic particles as they "drip" off their oversized neuclei. Through Boltzmann's constant, we humans perceive this velocity as temperature, and use this temperature to perform thermodynamic work. Most of our tools use electricity these days, so this temperature needs to be converted to electricity - and the most well-known method for doing this is a steam engine.

Steam engines produce tremendous torque and massive work output, but are very unsafe as they are prone to exploding. This is a centuries-old issue in railways, and a huge reason for the push away from steam locomotives and into the less-powerful but much safer diesel-electric, and gas-turbine locomotives which use so much fuel that they become a money pit. Likewise with navy vessels, steam boilers allow great speed but have been replaced by much safer diesel engines on virtually all craft. Steamboats are extremely uncommon in the 21st century. Nuclear warships were common, and then 2 sank - the US Navy has to monitor these to ensure they don't become critical disasters. A nuclear warship requires a huge team of highly-trained engineers, and costs so much that they only make sense when oil is more than $90 per barrel.

Failing to control the heat source has been the cause for every modern nuclear meltdown, because the technology relies on generating immense heat to boil water very quickly. A failure to correctly insert the control rods happened in Fukushima due to an electric grid outage from the earthquake, and in Chernobyl due to a mechanical malfunction. At Three Mile Island the heat caused a meltdown even with the control rods inserted correctly.

If steam isn't a viable technology, let's try this hot new tech: Steam!

Instead of avoiding the problematic technology, many want to patch it. One suggestion is to make the reactor fail-safe instead of fail-dangerous. Some of these designs include the Molten Salt reactor, which is designed to meltdown. Now we're compounding one problematic technology with the problematic part of another technology! With these powers combined, we can destroy the planet!

Other options involve using thorium - which is much more difficult to weaponize, as it's less potent. But thorium would still be coupled with the same problematic steam technology from the 17th century.

It's the 21st century - aren't there other options?

Betavoltaics are a low-power nuclear fission technology that have been used in satellites for more than 60 years. These capture beta radiation (aka fast electrons), creating electricity similar to a solar panel. Like batteries, each one can provide just a small charge, but hundreds of these could power a house or maybe even a car. A downside for the energy industry is that they last for decades, so you won't have customers stopping by daily to fill up their betavoltaic systems. For the consumer, it would be like a solar-powered car or house - even at nighttime or in a heavy storm. Future reasearch could unlock "alphavoltaics" which use the helium ion alpha particle in the same way.

Alpha-Betavoltaic Circuit - Like putting 2 terminals at either end of a radiation source, and place a permanent magnet nearby to direct helium ion alpha particles towards one end, and naked electron beta particles towards another end, creating an electric circuit.

Gamma ray capture - this would unlock both numerous nuclear fission energy sources, as well as possibly antimatter energy sources. Matter-antimatter annihilation releases immense energy, with all of it split between just 2 photons. Gamma rays are known for causing "bit flips" - adding an electric charge to a memory cell, so it changes from a 0 to a 1. This issue is so prevalent in our terrestrial electronics that we use basic error correction to make it invisible, and so bad in our orbital electronics that satellites run multiple sets of hardware that constantly reload each others memory cells. Treating these memory cells like capacitors could allow us to usefully discharge them instead, extracting some of the overwhelming energy of these photons.

Internal-criticality engine - This would resemble an internal combustion engine, with a nearly-critical mass on each piston and another at the end of each cylinder. The pistons would push the two masses together until the forces of the subatomic particles become critical enough to repel the piston.

Fusion - this is the new "holy grail" for the industry, but will be more interesting as a materials source than as a power source. It has an even more massive heat problem, as its operational goal is self-sustaining solar temperatures and pressures.

I don't even go here...

As a nuclear energy industry outsider, I'm likely just scratching the surface for options. Despite the vast environmental messaging prevalent in the modern ad-scape, US American choices are still heavily engineered to sell oil, gasoline, and other petroleum products on a daily basis. Did you know that 50 MPG cars have been common in Europe for decades, but are still extremely rare in the USA?



01/20/2022 - Miles O'Kilometer.

//Adding a conversion calculator to the 01/12/2022 collection: Miles to kilometers and back.

mi <> km


01/17/2022 - Porta cipher.

//This is a neat reciprocal cipher - copy the output and paste back into the input and it will show your original message. This means you can use the same tool (like this page) to both encrypt and later decrypt the message.

//Key:
//Input:
//Output:

//Code

//This took a while to sort out. First, set up the two halves of the alphabet. Then, make everything uppercase and remove all spaces. This should be extended to either remove all punctuation, or also handle punctuation in the halves of the alphabet.

var alpha = ["A","B","C","D","E","F","G","H","I","J","K","L","M"];
var bet = ["N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];

function getPorta(message,key = "PORTA"){
    message = message.toUpperCase().replace(/ /g,"");
    key = key.toUpperCase();
    var out = "";

//Foreach letter, set up msgLetter as the current letter, keyLetterCode as the current key's code, and msgLetterCode as its code. The key uses a modulus (%) to infinitely wrap around it.

    for (messageIndex=0; messageIndex < message.length; messageIndex++) {
      var msgLetter = message.substring(messageIndex, messageIndex+1);
      var keyLetterCode = Math.floor((key.substring( messageIndex%key.length, messageIndex%key.length+1 ).charCodeAt(0)-65)/2);
      var msgLetterCode = msgLetter.charCodeAt(0)-65;

//This part rotates the lower alphabet to the correct location - first it slices the alphabet from the key index to the end, then pushes those before the key index into it. I had this as another function, but Javascript's async nature means that would finish after the code below it - luckily it wasn't very long and didn't clutter up the function much.

      var portaBeta = bet.slice(keyLetterCode, bet.length);
      for (arrayFillIndex=0; arrayFillIndex < keyLetterCode ;arrayFillIndex++) {
        portaBeta.push(bet[arrayFillIndex]);
      }

//The last part finds the msgLetter's location in one array, and outputs that same location in the other array.

      if (msgLetterCode >12) {
        out += alpha[portaBeta.indexOf(msgLetter)];
      } else {
        out += portaBeta[alpha.indexOf(msgLetter)];
      };// end if keyLetterCode
    }
    return out;
}; //end getPorta

//And that gives us a reversable cipher. Next steps involve working to replace the key by inferring it from the message somehow. The hurdle here is getting it to be reversable.



01/16/2022 - Relativistic equations.

//Lorentz Factor

//This is all based on the Lorentz factor, which describes how matter changes as it moves through space:

function returnLorentzFactor(Velocity) {
    return (1 / Math.sqrt(1- (Math.pow(Velocity,2) / c_Squared)));
}

//>returnLorentzFactor()

//>

//Lorentz Multiple

//Next, we multiply the input velocity by the factor by its respective velocity, to give relativistic speed as seen by an observer.

function returnLorentzMultiple(Velocity) {
    return (Velocity * (returnLorentzFactor(Velocity)));
}

//> returnLorentzMultiple()

//>

//Time Dilation

//One of the more interesting impacts of using relativistic speed is living longer than those who don't use it.

function returnTimeDilation(timeObserver,Velocity) {
    return (returnLorentzFactor(Velocity)) * timeObserver;
}

//> returnTimeDilation(, )

//>

//Length Contraction

//Another interesting impact is that you are thinner while using relativistic speeds, compared to someone with a lesser speed. Just don't lay down or you'll become shorter instead.

function returnLengthContraction(Length,Velocity) {
    return Length/(returnLorentzFactor(Velocity));
}

//> returnLengthContraction(, )

//>

//Relativistic Mass

//Sadly the temporary thinness is accompanied by temporary weight gain:

function returnRMass(restMass,Velocity) {
    return (returnLorentzFactor(Velocity))*restMass;
}

//> returnRMass(, )

//>

//Relativistic Momentum

//Being slightly more massive means you're slightly more momentous.

function returnRMomentum(Mass,Velocity) {
    return (1 / Math.sqrt(1+ (Math.pow(Velocity,2)/(Math.pow(Mass,2) * c_Squared))));
}

//> returnRMomentum(, )

//>

//Relativistic kinetic energy

//This is a form of E2=(mc2)2+(mv)2(c)2, which leaves out the momentum half.

function returnRKE(restMass,Velocity) {
    return ((returnLorentzFactor(Velocity)) -1) * restMass * c_Squared;
}

//> returnRKE(, )

//>

//Escape Velocity

//Escape velocity is the minimum speed a ballistic object needs to escape from a massive body such as Earth. It represents the kinetic energy that, when added to the object's gravitational potential energy, (which is always negative) is equal to zero.

function returnEscapeVelocity(restMass = EarthMass,Radius = EarthRadius) {
    return returnRMomentum (Math.sqrt( (2 * GravitationalConstant * restMass) / Radius))
}

//> returnEscapeVelocity(, )

//>



01/14/2022 - The other half completes the whole.

Write Element puts the value into any HTML element. Just give it the Element ID and it will display the text or value inside that element.

function writeElement($elementId,$source) {
    var $elementType = JSON.stringify(document.getElementById($elementId).type);
    if (($elementType == '"text"')
    || ($elementType == '"select-one"')
    || ($elementType == '"number"')) {

Similarly simple - gather the element's type, and update the appropriate value based on this.

      document.getElementById($elementId).value = $source;
    } else {
      if (document.getElementById($elementId).tagName == 'IMG') {
        document.getElementById($elementId).src = $source;
      }
      else {
        document.getElementById($elementId).innerText = $source;
      }
    }; // end if elementType
}; // end writeElement

This is the counterpart to yesterday's Read Element, and looks very similar because it performs the inverse function. This one excludes Textarea but includes Image, likely because I haven't used readElement on images, nor writeElement on textareas.



01/13/2022 - Frustration is the stepmother of invention.

Read Element gathers the value from any HTML element. Just give it the Element ID and it will give you the text or value displayed inside that element.

function readElement($elementId) {
    var $elementType = JSON.stringify(document.getElementById($elementId).type);
    if (($elementType == '"text"')
    || ($elementType == '"textarea"')
    || ($elementType == '"select-one"')
    || ($elementType == '"number"')) {

Not too complex - gather the element's type, and return the appropriate value based on this.

      return document.getElementById($elementId).value;
    } else {
      return document.getElementById($elementId).innerText;
    }; // end if elementType
}; // end readElement

I made this because I find it maddening for different element types to have the same data concept under different variable names. So I hid it in the imperative layer. My software design is generally to create an imperative layer, and a declarative layer on top. Read Element is a key imperative function in this paradigm, being widely used to simplify both the creation and the operation of many pages.



01/12/2022 - Conversion calculators.

Starting with Ft to Meter and back:

ft <> meter

WebCoif makes this easy, with two functions that simplify reading and writing so much that you might have an emotional reaction. First, Read Element gets the value from any element, transform, and Write Element to output the value into the other field.

    writeElement('meterInput',(readElement('ftInput')*0.3048))

And the complement for the other input field:

    writeElement('ftInput',(readElement('meterInput')*3.28084))

Added to the oninput of each input field links the two fields in an easy to use but powerful way.

lb <> kg
tons <> metric tons
gal <> liter
mph <> km/h
ft/lb <> newton-meter

WebCoif is currently part of Gilgamech.js.



01/09/2022 - CSS popupdate.

Update to the 01/02/2022 post, to clean up formatting. First, lighten the default background from the solid blue to a color closer to sky blue:

    .popup {
      background: #9df;
    }

Next, a little trick to keep the popup div centered is to center the text after setting the margins. These margins keep the popup within the window almost all of the time:

    .popup span {
      position: absolute;
      /*margin: top right bottom left*/
      margin: 15px 5px 0 -30px;
      text-align: center;
      ...
    }

The left margin pushes the popup span about 4 letters to the left of the parent span, so at least a few letters overlap. And the right margin setting forces the popup span to remain just inside the window. Still looking for a "Goldilocks" setting that's not too wide for Mobile and not too narrow for Desktop.



01/08/2022 - Relocation code snippet.

    [string]$DefaultSecurityGroupID = "DEFAULTSG"
    $currentServer = (Get-EC2Instance).Instances
    [int]$CurrentSecurityGroupName = (Get-EC2SecurityGroup | where {$_.description -match "Created on"}).GroupName
    $newSecurityGroupID = New-RdpSecurityGroup
    Edit-EC2InstanceAttribute -InstanceId $currentServer.InstanceId -Group @($DefaultSecurityGroupID, $newSecurityGroupID)
    Remove-EC2SecurityGroup -GroupId (Get-EC2SecurityGroup -GroupName $CurrentSecurityGroupName).GroupId -Force


01/07/2022 - When two (sets of columnar data) become one.

//ColumnMath transforms data from 2 columns, or one column and a constant, into a third column. This builds most of the tables on the Ingame Item page from just two data columns (cost and quantity) for recent games. It can perform basic math on two columns in a table and put the output in another column of the same table. It can even read from 2 different tables and write to a 3rd, can add the output column as a new column in the table (with specified or autogenerated name), and can add min/max formatting. Rows are sortable with the numerical sort function whose description is coming soon - that's an automatic function of addColumn, whose description is also coming soon.

//Here are the inputs:
ParameterTypeDescriptionValid InputsNotes
TableAidstringInput A's table ID.Table ID on page.This will be the numerator for Divide and Percent functions.
inputAColintegerInput A's column number in Table A.0 up to number of columns -1.
TableBidstringInput B's table ID.Table ID on page (or "" to to specify a constant for Input B instead.)This will be the denominator for Divide and Percent functions, and negated for subtraction functions.
inputBColintegerInput B's column number in Table B.0 up to number of columns -1.
(If using a constant for Input B, put that constant here instead.)
rowBAdjintegerAdjust Input B down by this many rows.0 down to negative table length.Used when comparing the current cell to the cell beneath.
TableOutidstringOutput table ID.Table ID on page.
outputColintegerDescriptionOutput column number in the table.Starting from 0 (zero). If this is an existing column, its contents will be replaced with this function's output. If this is 1 + the highest numbered column (aka this is 2 when there are 2 columns, as they will be 0 and 1) then it will add a new column to the table.
mathOperationstringDescription"add","subtract","multiply","divide","percent","none"Percent is Divide also by 100.
roundDigitsintegerDescription0 to 16 (or higher?)Rounds the output to the specified digits. (Low-value numbers might show oddly when rounded too much, and this works best at 2 and above.)
formatMaxOutputBinaryDescriptionTrue or false.Perform formatMax (green gradient based on ratio between min and max) on column after transform, for better visualization.
newOutColumnNamestringHeader for new column. (Optional)(Any string)If outputCol would create a new column, this column becomes the column header. If blank, a new name is generated from Input column headers, any constant, and the math operation or verb.

//The first section gathers the table body data for Tables A, Output, and B if it's not a constant.

function columnMath(TableAid, inputACol, TableBid, inputBCol, rowBAdj, TableOutid, outputCol, mathOperation, roundDigits, formatMaxOutput, newOutColumnName) {
    var TableA = returnTablePart(TableAid,"TBODY");
    var TableB;
    var TableOut = returnTablePart(TableOutid,"TBODY");
    if (TableBid != "") {
      TableB = returnTablePart(TableBid,"TBODY");
    }

//The second section sets up the new column and name autogeneration. The name is autogenerated from the input column headers and the operation being performed.

    if (outputCol >= TableOut.children[0].children.length) {
      if (newOutColumnName == null) {
        var TableAHead = returnTablePart(TableAid,"THEAD");
        var TableBHead = returnTablePart(TableBid,"THEAD");
        var mathOperator;
        var mathVerb;
        switch(mathOperation) {
          case "none":
            mathOperator = "";
            mathVerb = "";
            break;
          case "add":
            mathOperator = " + ";
            mathVerb = " sum";
            break;
          case "subtract":
            mathOperator = " - ";
            mathVerb = " change";
            break;
          case "multiply":
            mathOperator = " * ";
            mathVerb = " multiple";
            break;
          case "divide":
            mathOperator = " / ";
            mathVerb = " rate";
            break;
          case "percent":
            mathOperator = " % ";
            mathVerb = " percent";
            break;
          default:
            mathOperator = " ? ";
            mathVerb = " error";
            break;
        }

//The second half of column generation extracts the input headers, and populates the new output column name, before adding the new column.

        if (TableBid != "") {
          if (TableAHead.children[0].children[inputACol].innerText == TableBHead.children[0].children[inputBCol].innerText) {
            newOutColumnName = TableAHead.children[0].children[inputACol].innerText + mathVerb;
          }
          else {
            newOutColumnName = TableAHead.children[0].children[inputACol].innerText + mathOperator + TableBHead.children[0].children[inputBCol].innerText;
          }
        }
        else {
          newOutColumnName = TableAHead.children[0].children[inputACol].innerText + mathOperator + numToTextNotation(inputBCol);
        }
      }
      addColumn(TableOutid,newOutColumnName);
    }

//This fourth section sets up the for loop to go row-by-row, transforming the inputs into the output. Step one is extracting the two values to transform - input A from TableA, and input B from either TableB or as a constant. The output row is also identified.

    for (var currentRow = (0-rowBAdj); currentRow < TableA.children.length; currentRow++) {
      var childrenOfA = TableA.children[currentRow];
      var childrenOfB;
      var childrenOfOut = TableOut.children[currentRow];
      var InputAText = textToNumNotation(childrenOfA.children[inputACol].innerText);
      var InputBText;
      if (TableBid != "") {
        childrenOfB = TableB.children[currentRow+rowBAdj];
        InputBText = textToNumNotation(childrenOfB.children[inputBCol].innerText);
      }
      else {
        InputBText = textToNumNotation(inputBCol);
      }

//This part performs the math operation and writes to the output cell.

      switch(mathOperation) {
        case "none":
          childrenOfOut.children[outputCol].innerText = childrenOfA.children[inputACol].innerText;
          break;
        case "add":
          childrenOfOut.children[outputCol].innerText = numToTextNotation((InputAText *1) + (InputBText*1)","roundDigits);
          break;
        case "subtract":
          childrenOfOut.children[outputCol].innerText = numToTextNotation((InputAText *1) - (InputBText*1)","roundDigits);
          break;
        case "multiply":
          childrenOfOut.children[outputCol].innerText = numToTextNotation((InputAText *1) * (InputBText*1)","roundDigits);
          break;
        case "divide":
          if ((InputAText *1) / (InputBText*1) == Infinity) {
            childrenOfOut.children[outputCol].innerText = 0;
          }
          else {
            childrenOfOut.children[outputCol].innerText = numToTextNotation((InputAText *1) / (InputBText*1)","roundDigits);
          }
          break;
        case "percent":
          if (((InputAText *1) / (InputBText*1)) *100 == Infinity) {
            childrenOfOut.children[outputCol].innerText = 0;
          }
          else {
            childrenOfOut.children[outputCol].innerText = numToTextNotation(((InputAText *1) / (InputBText*1)) *100 ","roundDigits);
          }
          break;
        default:
          // No transform nor write
          break;
      }

//Divide and Percent detect Infinity and replace with zero, which is the Javascript output for a divide-by-zero. This uses zero into an error numeral, as discussed at the end of a previous post.

//Finally, if this is the last row and formatMax is true, perform formatMax on the column. (Had to do this here to make it async.)

      if (currentRow+1 == TableOut.children.length+rowBAdj && formatMaxOutput == "true") {
        formatMax(outputCol,TableAid);
      }
    } //end for TableA
}

//Future updates will likely replace the TableOutid with the TableAid if it's omitted, try to merge the two switch sections in some way, and create the output table if it's not present.



01/05/2022 - Programmatically add CSS styles.

//Colorify Div wraps a search term in a span and applies a CSS class to it. The upper section creates the Regex variable","and removes from the search term the extra escaping necessary for dollar signs:

function colorifyDiv(divid","replaceWord","replaceClass) {
    var replaceRegex = new RegExp(replaceWord",""g");
    replaceWord = replaceWord.replace("\\$","$");

//This section reads from the div and performs the replacing:

    var str = document.getElementById(divid).innerHTML;
    str = str.replace(replaceRegex","'<span class="' + replaceClass + '">' + replaceWord + '</span>');

//The last section cleans up any accidental duplicates and writes back to the div:

    str = str.replace('<span class="<span class="'","'<span class="');
    str = str.replace('</span></span>','</span>');
    document.getElementById(divid).innerHTML = str;
}; // end colorifyDiv

//That might clobber other spans","so caution if you're a heavy span user","or if you're also using my popup code. This replaces a over 100 span tags in this and the previous posts.



01/04/2022 - I've got your number.

//As implied","here's Num to Text Notation. It takes a value (and rounding level) and outputs a textual number.

function numToTextNotation($inputObject,round) {

//The first section prepares a couple variables","and cleans up comma separators.

//Like other middle sections,this is inelegant but functional. This takes the numerical value","divides by the next lowest numerical significand","and rounds to the specified level. (Some numbers might show oddly when the round level is set too low.)

}

//Lastly","if the above results in NaN somehow","return a zero so we don't gum up the table. This makes zero basically a numerical error","so keep this in mind if you frequently use zero.



01/03/2022 - Thou Sand [You tiny rocks].

//Continuing the month's incidental theme of code sharing","here's a function that's seeing heavy use on the Ingame Item page - Text to Num Notation. This takes a textually-defined number","such as "10 million" and returns an integer","10000000. It has a partner function: Num to Text Notation","which reverses this","taking in i.e. 10000000 and outputting "10 million". Together","they allow using the same terminology as some games","removing confusion about which ingame item is which on the table.

//First is a short set of global variables to establish the value of each number name:

$thousand = 1000;
$million = $thousand *$thousand;
$billion = $million *$thousand;
$trillion = $billion *$thousand;
$quadrillion = $trillion *$thousand;
$quintillion = $quadrillion *$thousand;
$sixtillion = $quadrillion *$thousand;
$septillion = $sixtillion *$thousand;
$octillion = $septillion *$thousand;
$nonillion = $octillion *$thousand;
$decillion = $nonillion *$thousand;

//This needs refactoring","as it's getting kinda long","but has a relatively low range for many games. I use the English-speaking Short Scale because it's common where I live","but feel free to modify for your locality. Even though this is a Javascript function","I'm using the Powershell convention of dollar signs preceeding variable names is to maximize code portability","and these can be stripped from all code without issue.

//Now for the main event:

function textToNumNotation($inputObject) {

//This first section parses strings:

//This section swaps the textual significand with the numerical in the same variable. As with the short set of variables above","this section is functional but inelegant and needs refactoring. After that","it multiplies them and returns the output.

}

//Finally","handle numerical input by passing straight through","and handle other input types","also by passing through here","but error handling or other code could extend that section.



01/02/2022 - Tweaks to popup functionality.

/* The .popup class allows content to be hidden until it is tapped or mouseovered. 01/09/2022 update.*/

.popup {
}

/* This section holds formatting for the visible text","with a strong blue background to stand out. (This is still being tweaked for better artistic effect.) */

.popup span {
}

/* This section does 3 jobs: */

  1. /* The top lines specify the location. The position parameter needs to be set to absolute to separate from the parent div and be an actual popup. The vertical and horizontal locations are relative to the popup class's location on the page","and are supplemented by the margin:
    • /* 15px sets the popup at the lower edge for the default font size","but this can be changed to 20px for a small gap. */
    • /* 5vw gives a nice gap from the side of the page","for all but the widest tables. (This controls how wide the item can be","but doesn't push it further left. */
    • /* A lower margin isn't used right now. */
    • /* That sentence below is about 350px long","so pulling the horizontal location back near the start of this sentence means subtracting this from the left margin. */
    */
  2. /* The second section formats the popup box and text: solid black 1px border with a 3px buffer around the text","on a full-contrast background. */
  3. /* The display: none; part in the third section hides the popup. */
.popup:hover span {
}

/* Using the same trick as in the menu in the previous post","where hover inherits the display parameter of the visible outer tag. It's simpler than the Z-index method that the original example used","and obviates 3 CSS sections. Here's the HTML code and a working example: */

span class="popup"Both visible text and popup text go within the outer span.spanPopup text goes within the inner span./span/span

/* The original example was written for p tags","but works great with span: Both visible text and popup text go within the outer span.Popup text goes within the inner span. */



01/01/2022 - Updates to the menu.

/* With the new year brings a new section for blog pages in the menu. This site's menu is entirely CSS-based","using the HTML nav tag as a container within which the location style tags display","and relocate the menu divs to their appropriate place on the screen. */

nav {
}

/* Nav defines the style for the entire navigation bar","everything within the nav tag. */

nav ul {
}

/* Applies to all unordered lists inside the nav tag. The margin parameter specifies how far to the right of its parent section the dropdown will appear. */

nav ul li {
}

/* Applies to the list items within each unordered list. This one just provides formatting info","not modifying their position on the page","inherited from their ul tag. */

nav a {
}

/* Each menu item is wrapped in a tags. This uses the padding parameter to pad between items","and line-height to set the menu bar height","and the height of each dropdown menu item. */

nav a:hover {
}

/* Format a menu item when hovered over","with dark grey instead of black. */

nav ul ul {
}

/* Second-level ul tags contain the dropdown items","and so their top has to match the nav bar height. These are normally hidden by the display: none; parameter. */

nav ul li:hover > ul {
}

/* This is where the drop-down magic happens - when you mouseover a 2nd level li tag","the display: none; parameter of the ul tag within it is replaced with display: inherit;","letting that section inherit the display: visible; value and making them visible. Since the entire dropdown menu is contained within that li tag","the visibility remains inherited until the mouse moves off the drop-down menu","or it's tapped off of. The drop-down items (with a ▼ down arrow symbol) have the hreference parameter omitted","to enable mobile functionality. */

nav ul ul li {
}

/* Second level li tags contain the first level menus. The left parameter pulls the menu to the left edge of the drop-down item above it","and width is where the dropdown menu's width is set. It's unusual but functional to have the width and height set in different CSS sections. */

nav ul ul ul li {
}

/* Third level ul tags contain second level menus. These adjust the 2nd level menu to the side instead of below. */

Needed the RGB calculator to write this post.