4
\$\begingroup\$

I'm fine with any sort of answer that helps better my code -- I'm not necessarily looking for a full refactor. I have a script that converts input (IPv4 address, submask, cidr, etc.) into usable information to subnet networks.

If you need any more info here's the GH repo. It's only 350 lines with comments. (PS: I use object["property"] instead of object.property for closure compiler reasons.)

    "use strict"; // Just because.

if (window.navigator["standalone"]) {
    var noddy, remotes = false;
    document.addEventListener("click", function(event) {
        noddy = event.target;
        while (noddy.nodeName !== "A" && noddy.nodeName !== "HTML") { noddy = noddy.parentNode;
        }
        if (noddy.href.indexOf("http") !== -1 && (noddy.href.indexOf(document.location.host) !== -1 || remotes)) {
            event.preventDefault();
            document.location.href = noddy.href;
        }
    }, false);
}

function findios() {
    var iOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
    if (iOS === true && !window.navigator["standalone"]) {
        document.getElementById("iphoneinstall").setAttribute("class", "fish");
        document.getElementsByTagName("body")[0].setAttribute("style", "margin-top:40px;");
    } else {
        document.getElementById("iphoneinstall").setAttribute("class", "hidden");
    }
}
findios();

function openInstall() {
    element = document.getElementById("iphoneinstall");
    if (element.classList.contains("fish")) {
        element.setAttribute("class", "open");
    } else {
        element.setAttribute("class", "fish");
    }
}

function updateSite(event) {
    window.applicationCache.swapCache();
}
window.applicationCache.addEventListener('updateready', updateSite, false);

/* THIS DOES THE ACTUAL COMPUTATIONS */

function val() {
    // Just declaring some variables.
    var doc = document,
        submaskInput = doc.form["submask"].value,
        ipInput = doc.form["ip"].value,
        submask, base, index, theBigString, netFinal, netInit;

    // Determine the type of input
    if (submaskInput <= 32) { // less than or equal to = cidr
        base = submaskInput;
        // parseInt because if it's CIDR notation then we need to convert the string input to an int
        submask = getSubmask(parseInt(submaskInput, 10));
    }
    if (submaskInput.split(".").length === 4) {
        // if you can split the input ip into four parts it's a submask
        base = getCidr(submaskInput);
        submask = submaskInput;
    }
    if (doc.form["cb"].checked || submaskInput > 32) { // greater than = host or checked checkbox
        base = getCidrFromHost(submaskInput);
        submask = getSubmask(base);
    }
    if (base === 'undefined' || isNaN(base) || base === null) {
        // if base isn't valid then do nothing
        return null;
    }

    var ipInputArray = ipInput.split("."),
        submaskInputArray = submask.split(".");

    function getCidrFromHost(input) {
        // as long as the number of hosts isn't 0, find (log2(hosts)), round up, and subtract that from 32 to find the correct CIDR
        if (input !== 0) {
            input = (32 - (Math.ceil((Math.log(input)) / (Math.log(2)))));
        }
        return input;
    }

    function getSubmask(input) {
        // self explanatory
        if (input === 0) {return "0.0.0.0";}
        if (input === 1) {return "128.0.0.0";}
        if (input === 2) {return "192.0.0.0";}
        if (input === 3) {return "224.0.0.0";}
        if (input === 4) {return "240.0.0.0";}
        if (input === 5) {return "248.0.0.0";}
        if (input === 6) {return "252.0.0.0";}
        if (input === 7) {return "254.0.0.0";}
        if (input === 8) {return "255.0.0.0";}
        if (input === 9) {return "255.128.0.0";}
        if (input === 10) {return "255.192.0.0";}
        if (input === 11) {return "255.224.0.0";}
        if (input === 12) {return "255.240.0.0";}
        if (input === 13) {return "255.248.0.0";}
        if (input === 14) {return "255.252.0.0";}
        if (input === 15) {return "255.254.0.0";}
        if (input === 16) {return "255.255.0.0";}
        if (input === 17) {return "255.255.128.0";}
        if (input === 18) {return "255.255.192.0";}
        if (input === 19) {return "255.255.224.0";}
        if (input === 20) {return "255.255.240.0";}
        if (input === 21) {return "255.255.248.0";}
        if (input === 22) {return "255.255.252.0";}
        if (input === 23) {return "255.255.254.0";}
        if (input === 24) {return "255.255.255.0";}
        if (input === 25) {return "255.255.255.128";}
        if (input === 26) {return "255.255.255.192";}
        if (input === 27) {return "255.255.255.224";}
        if (input === 28) {return "255.255.255.240";}
        if (input === 29) {return "255.255.255.248";}
        if (input === 30) {return "255.255.255.252";}
        if (input === 31) {return "255.255.255.254";}
        if (input === 32) {return "255.255.255.255";}
    }

    function getCidr(input) {
        //self explanatory
        if (input === "0.0.0.0") {return 0;}
        if (input === "128.0.0.0") {return 1;}
        if (input === "192.0.0.0") {return 2;}
        if (input === "224.0.0.0") {return 3;}
        if (input === "240.0.0.0") {return 4;}
        if (input === "248.0.0.0") {return 5;}
        if (input === "252.0.0.0") {return 6;}
        if (input === "254.0.0.0") {return 7;}
        if (input === "255.0.0.0") {return 8;}
        if (input === "255.128.0.0") {return 9;}
        if (input === "255.192.0.0") {return 10;}
        if (input === "255.224.0.0") {return 11;}
        if (input === "255.240.0.0") {return 12;}
        if (input === "255.248.0.0") {return 13;}
        if (input === "255.252.0.0") {return 14;}
        if (input === "255.254.0.0") {return 15;}
        if (input === "255.255.0.0") {return 16;}
        if (input === "255.255.128.0") {return 17;}
        if (input === "255.255.192.0") {return 18;}
        if (input === "255.255.224.0") {return 19;}
        if (input === "255.255.240.0") {return 20;}
        if (input === "255.255.248.0") {return 21;}
        if (input === "255.255.252.0") {return 22;}
        if (input === "255.255.254.0") {return 23;}
        if (input === "255.255.255.0") {return 24;}
        if (input === "255.255.255.128") {return 25;}
        if (input === "255.255.255.192") {return 26;}
        if (input === "255.255.255.224") {return 27;}
        if (input === "255.255.255.240") {return 28;}
        if (input === "255.255.255.248") {return 29;}
        if (input === "255.255.255.252") {return 30;}
        if (input === "255.255.255.254") {return 31;}
        if (input === "255.255.255.255") {return 32;}
    }

    function calculateHosts(hv) {
        hv = hv || 0; // zero out hv
        if (hv >= 2) {
            hv = (Math.pow(2, (32 - hv)));
            // 2^(total bits - on bits) = off bits
        }
        return hv;
    }

    function calculateSubnets(input) {
        // this is black magic >:)
        var valToSubtractFromInput = !index ? 0 : index < 3 ? Math.pow(2, index + 2) : 24;
        return~~ Math.pow(2, (input - valToSubtractFromInput)) + " subnets";
    }

    /*

    The above function does this:

        If there's no var index, which is the index of the first octect !== "255", then the value we're subtracting from the input (CIDR) is 0. If there IS, and it's less than 3, then we take 2, raise it to the power of index + 2 and subtract that from the input. If both are false, then the value is 24.

        We return what is essentially (but not exactly) Math.floor (~~) of 2 to the power of the input - the value we previously found. That = the amount of subnets

    */


    function onBits(bits) {
        // Turns the CIDR into 1s and 0s
        var one = "1",
            two = "0";

        // Adds "1"s or "0"s until we've added as many as there are bits (CIDR)
        for (var i = ""; i.length < bits;) {
            i += one;
        }
        // Same, but in reverse so we can count the off bits
        for (var v = ""; v.length < (32 - bits);) {
            v += two;
        }
        var binarystring = i + v;
        // Inserts a period after every 8th character
        return binarystring.replace(/\B(?=(\d{8})+(?!\d))/g, ".");
    }

    function findClass(ip) {
        if (ipInputArray.length === 4) {
            if (!ip || ip < 0 || typeof ip === 'undefined') {
                return "No Valid IP Entered";
            }
            if (ip < 128) {
                return "Class A";
            }
            if (ip < 192) {
                return "Class B";
            }
            if (ip < 224) {
                return "Class C";
            }
            if (ip < 240) {
                return "Class D";
            }
            if (ip < 256) {
                return "Class E";
            }
        } else {
            return "No Valid IP Entered";
        }
    }

    for (var j = 0; j < submaskInputArray.length; j++) {
        // finds the first octet not equal to 255
        if (submaskInputArray[j] !== "255") {
            index = j;
            break;
        }
    }

    function getNetworkRange(cider) {
        var init, network, broadcast, modResult = cider % 8;
        if (modResult) {
            init = (Math.pow(2, (8 - modResult)));
            network = ((Math.floor(ipInputArray[index] / init)) * init);
            broadcast = (network + (init - 1));
        } else if (cider === 32 || cider === 31) {
            network = "N/A";
            broadcast = "N/A";
        } else {
            init = 128;
            network = ((Math.floor(ipInputArray[index] / init)) * init);
            broadcast = "255";
        }
        return [network, broadcast];
    }

    function getEnds(input) {
        var netInit = getNetworkRange(base),
            netFinal = placeRangeCorrectly(netInit[0], netInit[1]);
        return netFinal;
    }

    function placeRangeCorrectly(network, broadcast) {
        var networkString, broadcastString, networkStringInitial = "",
            broadcastStringInitial = "";
        for (var i = 0; i < index; i++) {
            networkStringInitial += ipInputArray[i] + ".";
            broadcastStringInitial += ipInputArray[i] + ".";
        }
        networkString = networkStringInitial + network;
        broadcastString = broadcastStringInitial + broadcast;
        if (index === 0) {
            networkString += ".0.0.0";
            broadcastString += ".255.255.255";
        }
        if (index === 1) {
            networkString += ".0.0";
            broadcastString += ".255.255";
        }
        if (index === 2) {
            networkString += ".0";
            broadcastString += ".255";
        }
        if (!index) {
            networkString = ipInput;
            broadcastString = ipInput;
        }
        return theBigString = [networkString, broadcastString];
    }

    function datRangeYo() {
        var networkOctet = theBigString[0].split("."),
            broadcastOctet = theBigString[1].split("."),
            firstUsable = (parseInt(networkOctet[3], 10) + 1),
            lastUsable = (parseInt(broadcastOctet[3], 10) - 1),
            fullUsableRange = networkOctet.slice(0, -1).join(".") + "." + firstUsable + " - " + broadcastOctet.slice(0, -1).join(".") + "." + lastUsable;
            if(!index) {
                fullUsableRange = ipInput + " - " + ipInput;
            }
        return fullUsableRange;
    }


    var ipiptoint = ipInputArray.map(function(x) {
        return parseInt(x, 10);
    });
    var iptohex = ipiptoint.map(function(v) {
        return ("00" + v.toString(16)).substr(-2);
    }).join(".");
    var wildcard = submaskInputArray.map(function(v) {
        return 255 - v;
    }).join(".");
    var hosts = calculateHosts(base),
        usable_hosts = (hosts - 2) > 0 ? (hosts - 2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : 0;

    // CIDR
    doc.getElementById("tablecidr").innerHTML = base;
    // Submask
    doc.getElementById("tablesubmask").innerHTML = submask;
    // Submask -> binary
    doc.getElementById("tablebinary").innerHTML = onBits(base);
    // # of hosts
    doc.getElementById("tablenumhosts").innerHTML = hosts.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + " (" + usable_hosts + " usable)";
    // # of subnets
    doc.getElementById("tablenumsubnets").innerHTML = calculateSubnets(base);
    // Wildcard mask
    doc.getElementById("tablewildcardmask").innerHTML = wildcard;
    // IP class
    doc.getElementById("tableipclass").innerHTML = findClass(ipInputArray[0]);
    // IP -> hex
    doc.getElementById("tableiptohex").innerHTML = iptohex.toUpperCase();
    // Network ID
    doc.getElementById("tablenetworkid").innerHTML = getEnds()[0];
    // Broadcast Address
    doc.getElementById("tablebroadcastaddress").innerHTML = getEnds()[1];
    // Network ranges
    doc.getElementById("tablenetworkrange").innerHTML = datRangeYo();

    function throwError() {
        var error = "No Valid IP Entered",
            doc = document;
        // IP class
        doc.getElementById("tableipclass").innerHTML = error;
        // IP -> hex
        doc.getElementById("tableiptohex").innerHTML = error;
        // Network ID
        doc.getElementById("tablenetworkid").innerHTML = error;
        // Broadcast Address
        doc.getElementById("tablebroadcastaddress").innerHTML = error;
        // Network ranges
        doc.getElementById("tablenetworkrange").innerHTML = error;

    }

    if (ipInput.split(".").length !== 4 || ipInput === "") {
        throwError();
    }
    for (var i = 0; i < 4; i++) {
        var iptoint = parseInt(ipInputArray[i], 10);
        if (iptoint != ipInputArray[i] || iptoint < 0 || iptoint > 255) {
            throwError();
        }
        ipInputArray[i] = iptoint;
    }

}
window.onload = function() {
    document.getElementsByTagName("form")[0].onsubmit = function(evt) {
        evt.preventDefault();
        val();
        window.scrollTo(0, document.body.scrollHeight);
    };
    document.onkeypress = function keypressed(e) {
        var keyCode = (window.event) ? e.which : e.keyCode;
        if (keyCode == 13) {
            if (val()) {
                document.forms['form'].submit();
            }
        }
    };
};
\$\endgroup\$
5
\$\begingroup\$

A few notes until someone who knows JavaScript comes along...

  • function val() is a pretty bad name for a function. Functions should always have a descriptive name in a verb-noun form.
  • // Just declaring some variables. is a useless comment. Comments should explain why the code is doing something, not what it does (unless it's particularly obscure, then explain what and why.)
  • As it is, 32 is a magic number and should be replaced with a constant.
  • The functions getSubmask and getCidr smell a little funny. I think you should be using a Map for this. It allows you to store your Cidr as a Key and the SubMask as the value.
  • More useless comments in those functions. If it's //self explanatory, then there's no need for the comments.
  • This may be funny, but instead of saying it's "Black Magic", you should explain what's going on here. Mr. Maintainer will thank you. (And remember, Mr. Maintainer is likely to be YOU.)

    function calculateSubnets(input) {
        // this is black magic >:)
        var valToSubtractFromInput = !index ? 0 : index < 3 ? Math.pow(2, index + 2) : 24;
        return~~ Math.pow(2, (input - valToSubtractFromInput)) + " subnets";
    }
    
  • Oops! There is a comment explaining it. Below the function. That's not exactly where I'd expect to find it. It's inconsistent with the rest of your comments.

\$\endgroup\$
  • \$\begingroup\$ FYI it's not Java it's JavaScript two totally different thing! \$\endgroup\$ – Marc-Andre Jul 2 '14 at 18:08
  • \$\begingroup\$ Thanks for the review, sorry I got back to you so late. I changed 32 to a constant, which should help my code be more readable and (perhaps?) run smoother. I removed the useless comments (which I really do need to work on). Also, the two functions used to be a key and value, but the if statements proved to be faster. And IIRC they're shorter than using two arrays/objects. If I used one object/array, then I'd have to search via key values, and that's incredibly slow (like, low thousands of ops). Thanks again! \$\endgroup\$ – Eric Lagergren Jul 6 '14 at 4:36
  • \$\begingroup\$ Hmm that doesn't sound right. Key lookups should be faster. \$\endgroup\$ – RubberDuck Jul 6 '14 at 11:29
  • \$\begingroup\$ Sorry, meant lookup by value. Each 255.255.255.0 type string and 24 type number are paired, so one would have to return the other. I could only get one depending on the input, but I'll have to think about that. I'm trying to wrap my head around a stupid Python script at the moment, and my brain's fried :P \$\endgroup\$ – Eric Lagergren Jul 7 '14 at 3:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.