JSFiddle - React, Tailwind, and code Playground

by Gwyn Milcote

HTML

Testing all animal genes and images here. Punnett square at the bottom.

<p>
<span id='overall_stats'></span>
<br><span id='species_stats'></span>
<br>Refresh the page for a different animal!
</p>

<select id='animal_types'>
      <option disabled>--- Mammals</option>
    <option value='cat' selected>Cat</option>
    <option value='dog'>Dog</option>
    <option value='horse'>Horse</option>
    <option value='donkey'>Donkey</option>
    <option value='cow'>Cow</option>
    <option value='pig'>Pig</option>
    <option value='mouse'>Mouse</option>
    <option value='rat'>Rat</option>
    <option value='cavy'>Guinea pig</option>
    <option value='rabbit'>Rabbit</option>
    <option value='ferret'>Ferret</option>
    <option value='fox'>Fox</option>
      <option disabled>--- Birds</option>
    <option value='chicken'>Chicken (colour)</option>
    <option value='chicken2'>Chicken (shape)</option>
    <option value='pigeon'>Pigeon</option>
    <option value='budgie'>Budgie</option>
    <option value='cockatiel'>Cockatiel</option>
    <option value='zebrafinch'>Zebra finch</option>
    <option value='bengalese'>Bengalese finch</option>
    <option value='quail1'>Quail (Japanese)</option>
    <option value='quail2'>Quail (Chinese Painted)</option>
    <option value='quail3'>Quail (Bobwhite)</option>
      <option disabled>--- Reptiles</option>
    <option value='cornsnake'>Corn snake</option>
    <option value='ballpython'>Ball python</option>
    <option value='reticpython'>Reticulated python</option>
      <option disabled>--- Fish</option>
    <option value='goldfish'>Goldfish</option>
    <option value='medaka'>Medaka</option>
    <option value='zebrafish'>Zebrafish</option>
</select> 
<button id='random_animal'>Randomise!</button> 
<button id='cross_animal'>Cross 2 random parents</button>

<p>JSON: <span id='json_geno'>...</span></p>
Geno: <span id='output_geno' class='genotype_display'>...</span>
<p>Image: <div id='animal_image'>...</div></p>

Generating dropdowns...

CSS

img, div, button, p, span {
    box-sizing: border-box;
}

.genotype_display .alleles {
    display: inline-block;
    padding: 5px;
    border: 1px solid silver;
    cursor: pointer;
    position: relative;
}

.genotype_display .modifier {
    display: inline-block;
    padding: 5px;
    border: 1px solid silver;
}

.tooltip {
	display: none;
	position: absolute;
    top: 120%;
    left: calc(50% - 40px);
	border: 1px solid silver;
	background-color: #fff;
	padding: 10px;
    z-index: 2;
}

.section {
    border: 1px solid silver;
    padding: 10px;
    margin: 20px auto;
}

#select_container {
    display: flex;
    flex-flow: row wrap;
}

#select_container .select_holder {
    padding: 2px;
    width: 50%;
    border: 1px solid #eee;
}




/* PUNNETT SQUARE */

#punnett_table table {
    width: 100%;
    margin: 20px 0px;
    border-collapse: collapse;
}

#punnett_table table td {
    border: 1px solid grey;
    padding: 5px;
    text-align: center;
}

#punnett_table .cell_m {
    background-color: #E8F6FC;
}

#punnett_table .cell_f {
    background-color: #FCE8F5;
}

/* Other styles like paint/tortie? */


/* GENERAL */

.father_bg {
    background-color: powderblue;
}
.mother_bg {
    background-color: lightpink;
}

.bold {
    font-weight: bold;
}


/* COAT IMAGES */

.animal_coat {
    position: relative;
}
.animal_coat img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
.coat_full {
    width: 500px;
    height: 400px;
}
.coat_half {
    width: 250px;
    height: 200px;
}


/* FILTERS */

/* Shared across all animals */

.f_black { filter: invert(90%); }
.f_grey { filter: invert(50%); }
.f_silver { filter: invert(80%); }
.f_white { filter: invert(100%); }


.f_brown { filter: invert(65%) sepia(80%) saturate(272%) hue-rotate(337deg) brightness(98%) contrast(94%); }
.f_chocolate { filter: invert(32%) sepia(98%) saturate(815%) hue-rotate(355deg) brightness(89%) contrast(92%); }

.f_pink { filter: invert(85%) sepia(10%)...

JavaScript

const path = "http://mythstable.epizy.com/genetic/images/";

/*
* For assembling image layers, storing HTML.
*
* let c = new Coat(a, "thumb");
* c.addFilter("base", baseCSS);
* c.addImage("pangare", 0.5);
* c.addImage("roan");
* c.endCoat();
* let html = c.output();
*/
class Coat {
    constructor(path, size = ""){
        this.path = path;
        this.startCoat();
    }
    
    startCoat(){
        this.html = "";
    }
    
    // For fixed images.
    addImage(url, opacity = 1){
        this.html += "<img src='" + this.path + url + ".png'";
        if(opacity < 1){
            this.html += " style='opacity:" + opacity + "'";
        }
        this.html += ">\n";
    }
    
    // For black PNGs to be recoloured.
    addFilter(url, css, opacity = 1){
        this.html += "<img src='" + this.path + url + ".png' class='f_" + css + "'";
        if(opacity < 1){
            this.html += " style='opacity:" + opacity + "'";
        }
        this.html += ">\n";
    }
    
    endCoat(){
        this.html += "<img src='" + this.path + "line.png'>\n";
    }
    
    output(){
        return this.html;
    }
} 

/*
* All data and animal-specific funcs here.
* GENOME object contains loci + alleles, ordered by dominance.
* TERMS array will be mapped. {adultMale: "stallion"} etc.
* getPath() sets up the path for image URLs.
* getImage() returns image HTML and description array.
*/
const D = {
    horse: {
        name: "Horse", specific: "Equus ferus caballus",
        genome: {
            e: {
                name: "Extension", wt: "E+",
                alleles: ["E+", "e"]
            },
            a: {
                name: "Agouti", wt: "A+",
                alleles: ["A+", "a^t", "a"]
            },
            cr: {
                name: "Cream", wt: "cr+",
                alleles: ["Cr", "cr+", "prl"]
            },
            ch: {
                name: "Champagne", wt: "ch+",
                alleles: ["Ch", "ch+"]
            },
            g: {
             ...