]> foleosoft.com Git - QAnsel.git/commitdiff
Sat Aug 3 07:05:25 PM EDT 2024
authormiha-q <>
Sat, 3 Aug 2024 23:05:25 +0000 (19:05 -0400)
committermiha-q <>
Sat, 3 Aug 2024 23:05:25 +0000 (19:05 -0400)
src/gui.html [deleted file]
src/imports/dom-to-image.js [new file with mode: 0644]
src/imports/istina-editor.js
src/imports/measure.png [new file with mode: 0644]
src/index.html [new file with mode: 0644]
src/src.zip [new file with mode: 0644]

diff --git a/src/gui.html b/src/gui.html
deleted file mode 100644 (file)
index 1dd6937..0000000
+++ /dev/null
@@ -1,847 +0,0 @@
-<head>
-       <link href="imports/istina-editor.css" rel="stylesheet" />
-       <link href="imports/micromodal.css" rel="stylesheet" />
-       <script src="imports/estouls-api.js"></script>
-       <script src="imports/istina-editor.js"></script>
-       <script src="imports/chart.js"></script>
-       <script src="imports/micromodal.js"></script>
-       <style>
-               body { background-color: #1f1f1f; user-select: none; }
-               @font-face
-               {
-                       font-family: "SmileBASIC";
-                       src: url(imports/SMILEBASIC.ttf) format("truetype");
-               }
-               [class*=program] td
-               {
-                       font-family: 'Courier New', Courier, monospace;
-                       text-align: center;
-                       user-select: none;
-                       background-color: #353535;
-                       color: #9cdcfe;
-                       border-color: grey;
-                       font-weight: bold;
-                       border-style: solid;
-                       border-collapse: collapse;
-               }
-               .program-track
-               {
-                       table-layout: fixed;
-               }
-               [pidx*=',']:hover
-               {
-                       background-color: rgb(75, 0, 0);
-                       cursor: pointer;
-               }
-               /*.program-instr td:hover
-               {
-                       background-color: rgb(75, 0, 0);
-               }*/
-               textarea
-               {
-                       background-color: #353535;
-                       color: #dcdcaa;
-                       font-family: 'Courier New', Courier, monospace;
-                       width: 100%;
-                       height: 220px;
-               }
-               /*
-               .optionspanel
-               {
-                       text-align: right;
-                       width: 100%;
-               }
-               .option
-               {
-                       font-size: 24px;
-                       font-weight: bold;
-                       font-family: monospace;
-                       cursor: pointer;
-                       background-color: rgba(0,0,0,0);
-                       padding: 0;
-                       margin: 0;
-                       border: 0;
-                       display: inline-block;
-               }
-               #run { color: green; }
-               #setup { font-size: 28px; color: grey; }
-               #save,#help { font-size: 20px; }
-               #load,#clear,#apk { font-size: 20px; }
-               #apkwrapper { display: none; }
-               */
-
-               .top-button-row td:hover
-               {
-                       background-color: gray;
-                       cursor: pointer;
-               }
-
-               @keyframes animation-reveal
-               {
-                       from {
-                               clip-path: polygon(0% 0%, 0% 0%, 100% 0%, 100% 0%);
-                       }
-                       to {
-                               clip-path: polygon(0% 0%, 0% 100%, 100% 100%, 100% 0%);
-                       }
-               }
-       </style>
-</head>
-<body>
-       <input type="file" id="file-input" style="display: none;" />
-       <div class="modal micromodal-slide" id="modal-main" aria-hidden="true">
-               <div class="modal__overlay" tabindex="-1" data-micromodal-close>
-                       <div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-main-title" style="width: 640px;">
-                               <header class="modal__header">
-                                       <h2 class="modal__title" id="modal-main-title"></h2>
-                                       <button class="modal__close" aria-label="Close modal" data-micromodal-close style="cursor: pointer;"></button>
-                               </header>
-                               <main class="modal__content" id="modal-main-content"></main>
-                               <footer class="modal__footer">
-                                       <button class="modal__btn modal__btn-primary" id="modal-main-confirm">Confirm</button>&nbsp;
-                                       <button class="modal__btn" id="modal-main-cancel" onclick="closeModal();">Cancel</button>
-                               </footer>
-                       </div>
-               </div>
-       </div>
-       <table class="main-table" border="1" style="width: 100%;">
-               <tr>
-                       <td colspan="2">
-                               <table style="width: 100%; text-align: center;" class="top-button-row">
-                                       <tr>
-                                               <td onclick="menu('open');">๐Ÿ“‚</td>
-                                               <td onclick="menu('clear');">๐Ÿงน</td>
-                                               <td onclick="menu('save');">๐Ÿ’พ</td>
-                                               <td style="color: green;" id="run">โ–ถ</td>
-                                               <td onclick="menu('download');">๐Ÿ“ฅ</td>
-                                               <td>โ“</td>
-                                               <td onclick="menu('settings');" style="color: lightgray">โš™</td>
-                                       </tr>
-                               </table>
-                       </td>
-               </tr>
-               <tr>
-                       <td colspan="2">
-                               <div style="overflow-y: auto; overflow-x: auto; height: 256px; width: 0px;" oncontextmenu="return false;" class="program-track-container">
-                                       <table border=1 class="program-track"></table>
-                               </div>
-                       </td>
-               </tr>
-               <tr>
-                       <td style="width: 50%; height: 100%;">
-                               <istina-editor style="height: 100%;"></istina-editor>
-                       </td>
-                       <td style="width: 50%;">
-                               <canvas id="resultsChart"></canvas>
-                       </td>
-               </tr>
-       </table>
-       <table border=1 class="program-instr" style="display: none;" cellspacing="0" cellpadding="0"></table>
-       <table border=1 class="program-drag" style="visibility: hidden;"></table>
-       <script>
-               $ = x => { return document.querySelectorAll(x); };
-               IstinaEditor.init();
-               function overline(instr, pos, compare)
-               {
-                       var r;
-                       switch (instr)
-                       {
-                               case "CX":
-                                       switch (pos)
-                                       {
-                                               case "10": r = "<span style='text-decoration:overline;'>C</span>X"; break;
-                                               case "01": r = "C<span style='text-decoration:overline;'>X</span>"; break;
-                                       }
-                               break;
-                               case "SW":
-                                       switch (pos)
-                                       {
-                                               case "10": r = "<span style='text-decoration:overline;'>S</span>W"; break;
-                                               case "01": r = "S<span style='text-decoration:overline;'>W</span>"; break;
-                                       }
-                               break;
-                               case "CCX":
-                                       switch (pos)
-                                       {
-                                               case "100": r = "<span style='text-decoration:overline;'>C</span>CX"; break;
-                                               case "010": r = "C<span style='text-decoration:overline;'>C</span>X"; break;
-                                               case "001": r = "CC<span style='text-decoration:overline;'>X</span>"; break;
-                                       }
-                               break;
-                               case "CSW":
-                                       switch (pos)
-                                       {
-                                               case "100": r = "<span style='text-decoration:overline;'>C</span>SW"; break;
-                                               case "010": r = "C<span style='text-decoration:overline;'>S</span>W"; break;
-                                               case "001": r = "CS<span style='text-decoration:overline;'>W</span>"; break;
-                                       }
-                               break;
-                       }
-                       if (compare == undefined) return r;
-                       return compare.toString().replace(/[\"]/g, "'").trim() == r;
-               }
-
-               var INSTRUCTIONS =
-               [
-                       "X", "Pauli-X",
-                       "Y", "Pauli-Y",
-                       "Z", "Pauli-Z",
-                       "H", "Hadamard",
-                       "S", "Phase-S",
-                       "T", "Phase-T",
-                       "S<sup>&dagger;</sup>", "Phase-S Hermitian Transpose",
-                       "T<sup>&dagger;</sup>", "Phase-T Hermitian Transpose",
-                       "CX", "Controlled-NOT",
-                       "CCX", "Toffoli",
-                       "SW", "Swap",
-                       "CSW", "Fredkin",
-                       "Rx", "Rotation-X",
-                       "Ry", "Rotation-Y",
-                       "Rz", "Rotation-Z",
-                       "|0โŸฉ", "Qubit Set",
-                       "|1โŸฉ", "Qubit Reset",
-                       "๐Ÿ”", "Measure",
-                       "=1", "Conditional Set",
-                       "=0", "Conditional Reset"
-               ]; 
-               var ProgramTrack =
-               {
-                       SCALE: 64,
-                       STATE: [ [], [], [], [], [], [], [], [], [], [], [], [], [], [], [] ],
-                       DRAG: "",
-                       DRAGPOS: [0, 0],
-                       DRAGELEM: undefined,
-                       init: () =>
-                       {
-                               for (var i = 0; i < ProgramTrack.STATE.length; i++)
-                               {
-                                       ProgramTrack.STATE[i] = [];
-                               }
-                               ProgramTrack.draw();
-                       },
-                       decode: () =>
-                       {
-                               var width = 0;
-                               for (var i = 0; i < ProgramTrack.STATE.length - 1; i++)
-                               {
-                                       if (ProgramTrack.STATE[i].length > width)
-                                       {
-                                               width = ProgramTrack.STATE[i].length;
-                                       }
-                               }
-                               var ret = "";
-                               var maxQ = -1;
-                               var maxC = -1;
-                               for (var i = 0; i < width; i++)
-                               {
-                                       var ifblk = ProgramTrack.STATE[ProgramTrack.STATE.length - 1][i].trim();
-                                       if (ifblk.length > 0)
-                                       {
-                                               var a = ifblk.split("<sub>")[0].includes("0") ? 0 : 1;
-                                               var b = parseInt(ifblk.split("<sub>")[1].split("<")[0]);
-                                               ifblk = "if(c[" + b + "]==" + a + ") ";
-                                               if (b > maxC) maxC = b;
-                                       }
-                                       var cx = [0, -1, -1];
-                                       var sw = [0, -1, -1];
-                                       var ccx = [0, -1, -1, -1];
-                                       var csw = [0, -1, -1, -1];
-                                       for (var j = 0; j < ProgramTrack.STATE.length - 1; j++)
-                                       {
-                                               var instr = ProgramTrack.STATE[j][i].trim();
-                                               if (instr.length == 0) continue;
-                                               if (j > maxQ) maxQ = j;
-                                               switch (instr)
-                                               {
-                                                       case "X": ret += ifblk + "x q[" + j + "];\n"; break;
-                                                       case "Y": ret += ifblk + "y q[" + j + "];\n"; break;
-                                                       case "Z": ret += ifblk + "z q[" + j + "];\n"; break;
-                                                       case "H": ret += ifblk + "h q[" + j + "];\n"; break;
-                                                       case "S": ret += ifblk + "s q[" + j + "];\n"; break;
-                                                       case "T": ret += ifblk + "t q[" + j + "];\n"; break;
-                                                       case "S<sup>โ€ </sup>": ret += ifblk + "sdg q[" + j + "];\n"; break;
-                                                       case "T<sup>โ€ </sup>": ret += ifblk + "tdg q[" + j + "];\n"; break;
-                                                       case "|1โŸฉ": ret += ifblk + "set q[" + j + "];\n"; break;
-                                                       case "|0โŸฉ": ret += ifblk + "reset q[" + j + "];\n"; break;
-                                                       default:
-                                                               if (instr.includes("๐Ÿ”"))
-                                                               {
-                                                                       var num = parseInt(instr.split(">")[1].split("<")[0]);
-                                                                       if (num > maxC) maxC = num;
-                                                                       ret += ifblk + "measure q[" + j + "] -> c[" + num + "];\n";
-                                                               }
-                                               }
-                                               if (overline("CX", "10", instr)) { if (cx[1] != -1) { cx[0] = 1; } cx[1] = j; }
-                                               if (overline("CX", "01", instr)) { if (cx[2] != -1) { cx[0] = 1; } cx[2] = j; }
-                                               if (overline("SW", "10", instr)) { if (sw[1] != -1) { sw[0] = 1; } sw[1] = j; }
-                                               if (overline("SW", "01", instr)) { if (sw[2] != -1) { sw[0] = 1; } sw[2] = j; }
-
-                                               if (overline("CCX", "100", instr)) { if (ccx[1] != -1) { ccx[0] = 1; } ccx[1] = j; }
-                                               if (overline("CCX", "010", instr)) { if (ccx[2] != -1) { ccx[0] = 1; } ccx[2] = j; }
-                                               if (overline("CCX", "001", instr)) { if (ccx[3] != -1) { ccx[0] = 1; } ccx[3] = j; }
-                                               if (overline("CSW", "100", instr)) { if (csw[1] != -1) { csw[0] = 1; } csw[1] = j; }
-                                               if (overline("CSW", "010", instr)) { if (csw[2] != -1) { csw[0] = 1; } csw[2] = j; }
-                                               if (overline("CSW", "001", instr)) { if (csw[3] != -1) { csw[0] = 1; } csw[3] = j; }
-                                       }
-
-                                       if (cx[0]) { ret = "Duplicate `CX` at column " + (i + 1) + "."; break; }
-                                       if (cx[1] != -1 && cx[2] != -1) { ret += ifblk + "cx q[" + cx[1] + "], q[" + cx[2] + "];\n"; }
-                                       else if (cx[1] != -1 || cx[2] != -1) { ret = "Incomplete `CX` at column " + (i + 1) + "."; break; }
-
-                                       if (sw[0]) { ret = "Duplicate `SW` at column " + (i + 1) + "."; break; }
-                                       if (sw[1] != -1 && sw[2] != -1) { ret += ifblk + "swap q[" + sw[1] + "], q[" + sw[2] + "];\n"; }
-                                       else if (sw[1] != -1 || sw[2] != -1) { ret = "Incomplete `SW` at column " + (i + 1) + "."; break; }
-
-                                       if (ccx[0]) { ret = "Duplicate `CCX` at column " + (i + 1) + "."; break; }
-                                       if (ccx[1] != -1 && ccx[2] != -1 && ccx[3] != -1) { ret += ifblk + "ccx q[" + ccx[1] + "], q[" + ccx[2] + "], q[" + ccx[3] + "];\n"; }
-                                       else if (ccx[1] != -1 || ccx[2] != -1 || ccx[3] != -1) { ret = "Incomplete `CCX` at column " + (i + 1) + "."; break; }
-
-                                       if (csw[0]) { ret = "Duplicate `CSW` at column " + (i + 1) + "."; break; }
-                                       if (csw[1] != -1 && csw[2] != -1 && csw[3] != -1) { ret += ifblk + "cswap q[" + csw[1] + "], q[" + csw[2] + "], q[" + csw[3] + "];\n"; }
-                                       else if (csw[1] != -1 || csw[2] != -1 || csw[3] != -1) { ret = "Incomplete `CSW` at column " + (i + 1) + "."; break; }
-                               }
-                               if (!ret.includes("Duplicate") && !ret.includes("Incomplete"))
-                               {
-                                       if (maxC >= 0)
-                                       {
-                                               ret = "creg c[" + (maxC + 1) + "];\n" + ret;
-                                       }
-                                       if (maxQ >= 0)
-                                       {
-                                               ret = "qreg q[" + (maxQ + 1) + "];\n" + ret;
-                                               if (maxC >= 0)
-                                               {
-                                                       ret += "sample c;\n";
-                                               }
-                                               else
-                                               {
-                                                       ret += "sample q;\n";
-                                               }
-                                       }
-                                       
-                               }
-                               return ret;
-                       },
-                       drag: (e, erase, evt) =>
-                       {
-                               $(".program-instr")[0].style.display = "none";
-                               if (e.innerHTML.includes("Q") || e.innerHTML.includes("IF") || e.innerHTML.includes("..."))
-                               {
-                                       return;
-                               }
-                               if (evt.button == 2) 
-                               {
-                                       if (e.getAttribute("pidx") != null)
-                                       {
-                                               var s = e.getAttribute("pidx").split(",");
-                                               s[0] = parseInt(s[0]);
-                                               s[1] = parseInt(s[1]);
-                                               var state = ProgramTrack.STATE[s[0]][s[1]];
-                                               if (overline("CX", "10", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CX", "01");
-                                               if (overline("CX", "01", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CX", "10");
-                                               if (overline("SW", "10", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("SW", "01");
-                                               if (overline("SW", "01", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("SW", "10");
-                                               if (overline("CCX", "100", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CCX", "010");
-                                               if (overline("CCX", "010", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CCX", "001");
-                                               if (overline("CCX", "001", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CCX", "100");
-                                               if (overline("CSW", "100", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CSW", "010");
-                                               if (overline("CSW", "010", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CSW", "001");
-                                               if (overline("CSW", "001", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CSW", "100");
-                                               var instr = ProgramTrack.STATE[s[0]][s[1]].split("<")[0];
-                                               switch (instr)
-                                               {
-                                                       case "๐Ÿ”":
-                                                       case "=1":
-                                                       case "=0":
-                                                               var a = (parseInt(ProgramTrack.STATE[s[0]][s[1]].split("<sub>")[1]) + 1) % (ProgramTrack.STATE.length - 1);
-                                                               ProgramTrack.STATE[s[0]][s[1]] = instr + "<sub>" + a + "</sub>";
-                                                               break;
-                                               }
-                                               ProgramTrack.draw();
-                                       }
-                                       evt.preventDefault();
-                                       return false;
-                               }
-                               var px = ProgramTrack.SCALE + "px";
-                               var tr = "<tr style='height: " + px + "'>";
-                               var td = "<td style='width: " + px + "; height: " + px + "; cursor: grab;'>";
-                               if (ProgramTrack.DRAG == "" && e.innerHTML.trim().length > 0)
-                               {
-                                       var h = e.innerHTML.replace(/[<].*?[>]/g, "");
-                                       h = e.innerHTML;
-                                       ProgramTrack.DRAG = h;
-                                       ProgramTrack.DRAGPOS = [evt.clientX, evt.clientY];
-                                       $(".program-drag")[0].innerHTML = tr + td + h + "</tr></td>";                           
-                                       $(".program-drag")[0].style.visibility = "";
-                               }
-                               if (erase)
-                               {
-                                       var s = e.getAttribute("pidx").split(",");
-                                       s[0] = parseInt(s[0]);
-                                       s[1] = parseInt(s[1]);
-                                       ProgramTrack.STATE[s[0]][s[1]] = "";
-                                       e.innerHTML = "";
-                               }
-                       },
-                       draw: () =>
-                       {
-                               var drag_event = "onmousedown='ProgramTrack.drag(this, true, event);'";
-                               var px = ProgramTrack.SCALE + "px";
-                               var tr = "<tr style='height: " + px + "'>";
-                               var td = "<td style='min-width: " + px + "; width: " + px + "; height: " + px + ";' qubit='1'";
-                               td += ">";
-                               var html = "";
-                               for (var i = 0; i < ProgramTrack.STATE.length; i++)
-                               {
-                                       if (i == ProgramTrack.STATE.length - 1)
-                                       {
-                                               td = td.replace("qubit='1'", "qubit='0'");
-                                               html += tr + td.replace(drag_event, "");
-                                               html += "IF</td>";
-                                       }
-                                       else
-                                       {
-                                               td = td.replace("qubit='0'", "qubit='1'");
-                                               html += tr + td.replace(drag_event, "");
-                                               html += "Q<sub>" + (i + 1) + "</sub></td>";
-                                       }
-                                       td = td.replace(">", drag_event + ">");
-                                       for (var j = 0; j < ProgramTrack.STATE[i].length; j++)
-                                       {
-                                               var tdi = td.replace("<td", "<td pidx='" + i + "," + j + "' ");
-                                               html += tdi + ProgramTrack.STATE[i][j] + "</td>";
-                                       }
-                                       html += td.replace("<td", "<td pidx='" + i + "," + ProgramTrack.STATE[i].length + "' ").replace(drag_event, "") + "...</td></tr>";
-                               }
-                               $(".program-track")[0].innerHTML = html;
-                               
-                               td = "<td style='width: " + px + "; height: " + px + "; cursor: grab;'";
-                               td += " oncontextmenu='return false;'";
-                               td += " " + drag_event.replace("true", "false");
-                               var itr = 0;
-                               html = tr;
-                               for (var i = 0; i < INSTRUCTIONS.length; i += 2)
-                               {
-                                       html += td + "title='" + INSTRUCTIONS[i + 1] + "'" + ">" + INSTRUCTIONS[i] + "</td>";
-                                       itr++;
-                                       if (itr > 4 && i != INSTRUCTIONS.length - 2)
-                                       {
-                                               html += "</tr>" + tr;
-                                               itr = 0;
-                                       }
-                               }
-                               html += "</tr>";
-                               $(".program-instr")[0].innerHTML = html;
-                               IstinaEditor.setText(ProgramTrack.decode());
-                       }
-               };
-               
-               window.onload = function()
-               {
-                       ProgramTrack.draw();
-                       //$(".program-track")[0].parentNode.style.height = Math.round($(".program-instr")[0].getBoundingClientRect().height) + "px";
-                       $(".program-track")[0].parentNode.style.width = "100%";
-                       //Math.round($(".program-instr")[0].getBoundingClientRect().width) + "px";
-                       $(".program-track-container")[0].style.height = (ProgramTrack.SCALE*4 + 10) + "px"
-                       $(".program-track-container")[0].style.width = $(".program-track-container")[0].parentNode.getBoundingClientRect().width + "px"
-               };
-
-               document.body.addEventListener("keydown", e =>
-               {
-                       modalFixes();
-               });
-               document.body.addEventListener("mousemove", e =>
-               {
-                       modalFixes();
-                       var x = $(".program-drag")[0];
-                       var w = parseFloat(getComputedStyle(x).width.replace("px", "")) / 2;
-                       var h = parseFloat(getComputedStyle(x).height.replace("px", "")) / 2;
-                       x.style.position = "absolute";
-                       x.style.left = (e.clientX - w) + "px";
-                       x.style.top = (e.clientY - h) + "px";
-               });
-               document.body.addEventListener("mouseup", e =>
-               {
-                       if ($(".program-drag")[0].style.visibility == "hidden")
-                       {
-                               var trackerElem = document.elementFromPoint(e.clientX, e.clientY);
-                               var str = trackerElem.getAttribute("pidx");
-                               if (str) {} else {str = ""};
-                               if (str.includes(",") && (trackerElem.innerHTML == "..." || trackerElem.innerHTML == ""))
-                               {
-                                       ProgramTrack.DRAGELEM = trackerElem;
-                                       $(".program-instr")[0].style.display = "none";
-                                       $(".program-instr")[0].className = $(".program-instr")[0].className.replace("menu-hidden", "").trim();
-                                       $(".program-instr")[0].className += " menu-reveal";
-                                       $(".program-instr")[0].style.position = "fixed";
-                                       $(".program-instr")[0].style.top = e.clientY + "px";
-                                       $(".program-instr")[0].style.left = e.clientX + "px";
-                                       $(".program-instr")[0].style.animation = "animation-reveal 0.2s forwards";
-                                       $(".program-instr")[0].style.display = "";
-                               }
-                               else
-                               {
-                                       $(".program-instr")[0].style.display = "none";
-                               }
-                               return;
-                       }
-                       $(".program-instr")[0].style.display = "none";
-                       ProgramTrack.draw();
-                       $(".program-drag")[0].style.visibility = "hidden";
-                       ProgramTrack.DRAG = "";
-
-                       var elem = document.elementFromPoint(e.clientX, e.clientY);
-                       var dist = Math.sqrt(Math.pow(ProgramTrack.DRAGPOS[0] - e.clientX, 2) + Math.pow(ProgramTrack.DRAGPOS[1] - e.clientY, 2));
-                       if (dist < 8) elem = ProgramTrack.DRAGELEM;
-                       var attr = elem.getAttribute("pidx");
-                       if (attr == null) return;
-                       attr = attr.split(",");
-                       attr[0] = parseInt(attr[0]);
-                       attr[1] = parseInt(attr[1]);
-                       var html = $(".program-drag td")[0].innerHTML;
-                       var qubit = parseInt(elem.getAttribute("qubit"));
-                       console.log(elem.innerHTML);
-                       switch (html)
-                       {
-                               case "=1": case "=0":
-                                       if (qubit) return;
-                               break;
-                               default:
-                                       if (!qubit) return;
-                       }
-                       var f = [0, 0, 0];
-                       switch (html)
-                       {
-                               case "CX":
-                                       if (ProgramTrack.STATE[attr[0]].length > attr[1])
-                                       {
-                                               for (var i = 0; i < ProgramTrack.STATE.length; i++)
-                                               {
-                                                       if (i != attr[0])
-                                                       {
-                                                               if (overline("CX", "10", ProgramTrack.STATE[i][attr[1]])) f[0] = 1;
-                                                               if (overline("CX", "01", ProgramTrack.STATE[i][attr[1]])) f[1] = 1;
-                                                       }
-                                               }
-                                       }
-                                       if (!f[0]) html = overline("CX", "10");
-                                       else html = overline("CX", "01");
-                               break;
-                               case "SW":
-                                       if (ProgramTrack.STATE[attr[0]].length > attr[1])
-                                       {
-                                               for (var i = 0; i < ProgramTrack.STATE.length; i++)
-                                               {
-                                                       if (i != attr[0])
-                                                       {
-                                                               if (overline("SW", "10", ProgramTrack.STATE[i][attr[1]])) f[0] = 1;
-                                                               if (overline("SW", "01", ProgramTrack.STATE[i][attr[1]])) f[1] = 1;
-                                                       }
-                                               }
-                                       }
-                                       if (!f[0]) html = overline("SW", "10");
-                                       else html = overline("SW", "01");
-                               break;
-                               case "CCX":
-                                       if (ProgramTrack.STATE[attr[0]].length > attr[1])
-                                       {
-                                               for (var i = 0; i < ProgramTrack.STATE.length; i++)
-                                               {
-                                                       if (i != attr[0])
-                                                       {
-                                                               if (overline("CCX", "100", ProgramTrack.STATE[i][attr[1]])) f[0] = 1;
-                                                               if (overline("CCX", "010", ProgramTrack.STATE[i][attr[1]])) f[1] = 1;
-                                                               if (overline("CCX", "001", ProgramTrack.STATE[i][attr[1]])) f[2] = 1;
-                                                       }
-                                               }
-                                       }
-                                       if (!f[0]) html = overline("CCX", "100");
-                                       else if (!f[1]) html = overline("CCX", "010");
-                                       else html = overline("CCX", "001");
-                               break;
-                               case "CSW":
-                                       if (ProgramTrack.STATE[attr[0]].length > attr[1])
-                                       {
-                                               for (var i = 0; i < ProgramTrack.STATE.length; i++)
-                                               {
-                                                       if (i != attr[0])
-                                                       {
-                                                               if (overline("CSW", "100", ProgramTrack.STATE[i][attr[1]])) f[0] = 1;
-                                                               if (overline("CSW", "010", ProgramTrack.STATE[i][attr[1]])) f[1] = 1;
-                                                               if (overline("CSW", "001", ProgramTrack.STATE[i][attr[1]])) f[2] = 1;
-                                                       }
-                                               }
-                                       }
-                                       if (!f[0]) html = overline("CSW", "100");
-                                       else if (!f[1]) html = overline("CSW", "010");
-                                       else html = overline("CSW", "001");
-                               break;
-                               case "๐Ÿ”": html = "๐Ÿ”<sub>0</sub>"; break;
-                               case "=1": html = "=1<sub>0</sub>"; break;
-                               case "=0": html = "=0<sub>0</sub>"; break;
-                       }
-
-                       ProgramTrack.STATE[attr[0]][attr[1]] = html;
-                       var mx = -1;
-                       for (var i = 0; i < ProgramTrack.STATE.length; i++)
-                       {
-                               if (ProgramTrack.STATE[i].length > mx)
-                               {
-                                       mx = ProgramTrack.STATE[i].length;
-                               }
-                       }
-                       for (var i = 0; i < ProgramTrack.STATE.length; i++)
-                       {
-                               while (ProgramTrack.STATE[i].length < mx)
-                               {
-                                       ProgramTrack.STATE[i][ProgramTrack.STATE[i].length] = "";
-                               }
-                       }
-                       ProgramTrack.draw();
-                       if (elem.innerHTML.trim() == "...")
-                       {
-                               $(".program-track-container")[0].scrollLeft = 999999;
-                       }
-               });
-               //Chart
-               var resultsChart = new Chart(document.getElementById("resultsChart"), {
-               type: 'bar',
-               data: {
-                       labels: [ ],
-                       datasets:
-                       [{
-                               label: '# of |1โŸฉ',
-                               data: [],
-                               borderWidth: 1
-                       }]
-               },
-               options: {
-                       scales: {
-                       y: {
-                               beginAtZero: true
-                       }
-                       }
-               }
-               });
-               function displayResults(results)
-               {
-                       var bitCount = Math.ceil(Math.log2(results.length));
-                       resultsChart.data.datasets[0].data = results;
-                       resultsChart.data.labels = [];
-                       for (var i = 0; i < results.length; i++)
-                       {
-                               resultsChart.data.labels[resultsChart.data.labels.length] = i.toString(2).padStart(bitCount, "0");
-                       }
-                       resultsChart.update();
-               }
-               function downloadResults()
-               {
-                       var csv = "";
-                       for (var i = 0; i < resultsChart.data.labels.length; i++)
-                       {
-                               csv += resultsChart.data.labels[i] + ",";
-                               csv += resultsChart.data.datasets[0].data[i] + "\r\n";
-                       }
-                       var a = document.createElement("a");
-                       a.href = "data:text/plain," + escape(csv);
-                       a.download = "data.csv";
-                       document.body.appendChild(a);
-                       a.click();
-                       a.remove();
-               }
-
-               $("#run")[0].addEventListener("click", () =>
-               {
-                       createBlocker();
-                       var key = localStorage.getItem("apikey");
-                       key = key ? key : "";
-                       if (key.split(":").length != 3)
-                       {
-                               $("#blocker-div")[0].remove();
-                               $("#blocker-img")[0].remove();
-                               errorModal("Please click the gear icon and input your API key.");
-                               return;
-                       }
-                       EstoulsAPI.username = key.split(":")[0];
-                       EstoulsAPI.apikey = key.split(":")[1];
-                       EstoulsAPI.endpoint = "https://" + key.split(":")[2];
-                       EstoulsAPI.request(IstinaEditor.getText()).then(x =>
-                       {
-                               $("#blocker-div")[0].remove();
-                               $("#blocker-img")[0].remove();
-                               if (x.success && x.response.trim().length > 0)
-                               {
-                                       var lines = x.response.trim().split("\n");
-                                       var type = lines[0].split("\t").length;
-                                       var results = [];
-                                       if (type == 3)
-                                       {
-                                               for (var i = 0; i < lines.length; i++)
-                                               {
-                                                       results[results.length] = parseInt(lines[i].split("\t")[1]);
-                                               }
-                                               displayResults(results);
-                                       }
-                                       else if (type == 2)
-                                       {
-                                               for (var i = 0; i < lines.length; i++)
-                                               {
-                                                       results[results.length] = parseFloat(lines[i].split("\t")[1].replace("%", "")) / 100.0;
-                                               }
-                                               displayResults(results);
-                                       }
-                                       else
-                                       {
-                                               errorModal(x.response, 1);
-                                       }
-                               }
-                               else if (x.success && x.response.trim().length == 0)
-                               {
-                                       errorModal(x.response, 1);
-                               }
-                               else
-                               {
-                                       errorModal(x.response, 1);
-                               }
-                       }).catch(x =>
-                       {
-                               $("#blocker-div")[0].remove();
-                               $("#blocker-img")[0].remove();
-                               errorModal(x.message, 1);
-                       });
-               });
-               MicroModal.init();
-               function menu(cmd)
-               {
-                       $(".program-instr")[0].style.display = "none";
-                       switch (cmd)
-                       {
-                               case "clear":
-                                       $("#modal-main-title")[0].innerHTML = "Clear Project";
-                                       $("#modal-main-content")[0].innerHTML = "<p>Are you sure you want to clear this project?</p>";
-                                       $("#modal-main-cancel")[0].style.display = "";
-                                       $("#modal-main-confirm")[0].style.display = "";
-                                       $("#modal-main-confirm")[0].setAttribute("onclick", "ProgramTrack.init(); closeModal();");
-                                       MicroModal.show("modal-main");
-                               break;
-                               case "settings":
-                                       var apikey = localStorage.getItem("apikey");
-                                       apikey = (apikey ? apikey : "").replace("'", "").replace("\"", "").replace("\n", "");
-                                       $("#modal-main-title")[0].innerHTML = "API Settings";
-                                       $("#modal-main-content")[0].innerHTML = "<p>Please enter your API key: </p><input type='text' id='apikey' />";
-                                       $("#apikey")[0].style.width = "100%";
-                                       $("#apikey")[0].addEventListener("click", () =>
-                                       {
-                                               $("#apikey")[0].selectionStart = 0;
-                                               $("#apikey")[0].selectionEnd = $("#apikey")[0].value.length;
-                                       });
-                                       $("#apikey")[0].addEventListener("keydown", e =>
-                                       {
-                                               if (e.key == "Enter")
-                                               {
-                                                       $("#modal-main-confirm")[0].click();
-                                               }
-                                       });
-                                       $("#apikey")[0].value = apikey;
-                                       $("#modal-main-confirm")[0].style.display = "";
-                                       $("#modal-main-confirm")[0].setAttribute("onclick", "localStorage.setItem('apikey', $('#modal-main-content input')[0].value); closeModal();");
-                                       MicroModal.show("modal-main");
-                               break;
-                               case "save":
-                                       var a = document.createElement("a");
-                                       a.href = "data:text/plain," + btoa(escape(JSON.stringify(ProgramTrack.STATE)));
-                                       a.download = "program.txt";
-                                       document.body.appendChild(a);
-                                       a.click();
-                                       a.remove();
-                               break;
-                               case "download":
-                                       downloadResults();
-                               break;
-                               case "open":
-                                       $("#file-input")[0].click();
-                               break;
-                       }
-               }
-               function errorModal(err, mode)
-               {
-                       if (mode == 1)
-                       {
-                               try
-                               {
-                                       err = err.replace(/[\n]/g, "<br />");
-                               }
-                               catch
-                               {
-                                       err = "Unknown server-side error.";
-                               }
-                       }
-                       $(".program-instr")[0].style.display = "none";
-                       $("#modal-main-confirm")[0].setAttribute("onclick", "closeModal();");
-                       $("#modal-main-cancel")[0].style.display = "none";
-                       $("#modal-main-title")[0].innerHTML = "An Error Occurred";
-                       $("#modal-main-content")[0].innerHTML = "<p style='color: red;'>" + err + "</p>";
-                       MicroModal.show("modal-main");
-               }
-               function closeModal()
-               {
-                       MicroModal.close();
-               }
-               $("#file-input")[0].addEventListener("change", e =>
-               {
-                       var reader = new FileReader();
-                       reader.addEventListener("load", () =>
-                       {
-                               try
-                               {
-                                       ProgramTrack.STATE = JSON.parse(unescape(atob(reader.result)));
-                                       ProgramTrack.draw();
-                               }
-                               catch
-                               {
-                                       errorModal("Could not open this file.");
-                               }
-                       }, false);
-                       if (e.target.files.length == 1)
-                       {
-                               reader.readAsText(e.target.files[0]);
-                       }
-               });
-               function createBlocker()
-               {
-                       var r = $(".main-table")[0].getBoundingClientRect();
-                       var div = document.createElement("div");
-                       div.setAttribute("id", "blocker-div");
-                       div.style.position = "absolute";
-                       div.style.top = Math.round(r.top) + "px";
-                       div.style.left = Math.round(r.left) + "px";
-                       div.style.width = Math.round(r.width) + "px";
-                       div.style.height = Math.round(r.height) + "px";
-                       div.style.backgroundColor = "black";
-                       div.style.opacity = "50%";
-                       var img = document.createElement("img");
-                       img.setAttribute("id", "blocker-img");
-                       img.src = "imports/spinner.gif";
-                       img.style.position = "absolute";
-                       img.style.width = "64px";
-                       img.style.height = "64px";
-                       img.style.left = Math.round(r.left + r.width / 2 - 32) + "px";
-                       img.style.top = Math.round(r.top + r.height / 2 - 32) + "px";
-                       document.body.appendChild(div);
-                       document.body.appendChild(img);
-               }
-               function modalFixes()
-               {
-                       if (getComputedStyle($("#modal-main")[0]).display == "none")
-                       {
-                               $(".istina-basis")[0].style.visibility = "";
-                       }
-                       else
-                       {
-                               $(".istina-basis")[0].style.visibility = "hidden";
-                       }
-               }
-       </script>
-       
-</body>
-
diff --git a/src/imports/dom-to-image.js b/src/imports/dom-to-image.js
new file mode 100644 (file)
index 0000000..27201ac
--- /dev/null
@@ -0,0 +1,769 @@
+(function (global) {
+    'use strict';
+
+    var util = newUtil();
+    var inliner = newInliner();
+    var fontFaces = newFontFaces();
+    var images = newImages();
+
+    // Default impl options
+    var defaultOptions = {
+        // Default is to fail on error, no placeholder
+        imagePlaceholder: undefined,
+        // Default cache bust is false, it will use the cache
+        cacheBust: false
+    };
+
+    var domtoimage = {
+        toSvg: toSvg,
+        toPng: toPng,
+        toJpeg: toJpeg,
+        toBlob: toBlob,
+        toPixelData: toPixelData,
+        impl: {
+            fontFaces: fontFaces,
+            images: images,
+            util: util,
+            inliner: inliner,
+            options: {}
+        }
+    };
+
+    if (typeof module !== 'undefined')
+        module.exports = domtoimage;
+    else
+        global.domtoimage = domtoimage;
+
+
+    /**
+     * @param {Node} node - The DOM Node object to render
+     * @param {Object} options - Rendering options
+     * @param {Function} options.filter - Should return true if passed node should be included in the output
+     *          (excluding node means excluding it's children as well). Not called on the root node.
+     * @param {String} options.bgcolor - color for the background, any valid CSS color value.
+     * @param {Number} options.width - width to be applied to node before rendering.
+     * @param {Number} options.height - height to be applied to node before rendering.
+     * @param {Object} options.style - an object whose properties to be copied to node's style before rendering.
+     * @param {Number} options.quality - a Number between 0 and 1 indicating image quality (applicable to JPEG only),
+                defaults to 1.0.
+     * @param {String} options.imagePlaceholder - dataURL to use as a placeholder for failed images, default behaviour is to fail fast on images we can't fetch
+     * @param {Boolean} options.cacheBust - set to true to cache bust by appending the time to the request url
+     * @return {Promise} - A promise that is fulfilled with a SVG image data URL
+     * */
+    function toSvg(node, options) {
+        options = options || {};
+        copyOptions(options);
+        return Promise.resolve(node)
+            .then(function (node) {
+                return cloneNode(node, options.filter, true);
+            })
+            .then(embedFonts)
+            .then(inlineImages)
+            .then(applyOptions)
+            .then(function (clone) {
+                return makeSvgDataUri(clone,
+                    options.width || util.width(node),
+                    options.height || util.height(node)
+                );
+            });
+
+        function applyOptions(clone) {
+            if (options.bgcolor) clone.style.backgroundColor = options.bgcolor;
+
+            if (options.width) clone.style.width = options.width + 'px';
+            if (options.height) clone.style.height = options.height + 'px';
+
+            if (options.style)
+                Object.keys(options.style).forEach(function (property) {
+                    clone.style[property] = options.style[property];
+                });
+
+            return clone;
+        }
+    }
+
+    /**
+     * @param {Node} node - The DOM Node object to render
+     * @param {Object} options - Rendering options, @see {@link toSvg}
+     * @return {Promise} - A promise that is fulfilled with a Uint8Array containing RGBA pixel data.
+     * */
+    function toPixelData(node, options) {
+        return draw(node, options || {})
+            .then(function (canvas) {
+                return canvas.getContext('2d').getImageData(
+                    0,
+                    0,
+                    util.width(node),
+                    util.height(node)
+                ).data;
+            });
+    }
+
+    /**
+     * @param {Node} node - The DOM Node object to render
+     * @param {Object} options - Rendering options, @see {@link toSvg}
+     * @return {Promise} - A promise that is fulfilled with a PNG image data URL
+     * */
+    function toPng(node, options) {
+        return draw(node, options || {})
+            .then(function (canvas) {
+                return canvas.toDataURL();
+            });
+    }
+
+    /**
+     * @param {Node} node - The DOM Node object to render
+     * @param {Object} options - Rendering options, @see {@link toSvg}
+     * @return {Promise} - A promise that is fulfilled with a JPEG image data URL
+     * */
+    function toJpeg(node, options) {
+        options = options || {};
+        return draw(node, options)
+            .then(function (canvas) {
+                return canvas.toDataURL('image/jpeg', options.quality || 1.0);
+            });
+    }
+
+    /**
+     * @param {Node} node - The DOM Node object to render
+     * @param {Object} options - Rendering options, @see {@link toSvg}
+     * @return {Promise} - A promise that is fulfilled with a PNG image blob
+     * */
+    function toBlob(node, options) {
+        return draw(node, options || {})
+            .then(util.canvasToBlob);
+    }
+
+    function copyOptions(options) {
+        // Copy options to impl options for use in impl
+        if(typeof(options.imagePlaceholder) === 'undefined') {
+            domtoimage.impl.options.imagePlaceholder = defaultOptions.imagePlaceholder;
+        } else {
+            domtoimage.impl.options.imagePlaceholder = options.imagePlaceholder;
+        }
+
+        if(typeof(options.cacheBust) === 'undefined') {
+            domtoimage.impl.options.cacheBust = defaultOptions.cacheBust;
+        } else {
+            domtoimage.impl.options.cacheBust = options.cacheBust;
+        }
+    }
+
+    function draw(domNode, options) {
+        return toSvg(domNode, options)
+            .then(util.makeImage)
+            .then(util.delay(100))
+            .then(function (image) {
+                var canvas = newCanvas(domNode);
+                canvas.getContext('2d').drawImage(image, 0, 0);
+                return canvas;
+            });
+
+        function newCanvas(domNode) {
+            var canvas = document.createElement('canvas');
+            canvas.width = options.width || util.width(domNode);
+            canvas.height = options.height || util.height(domNode);
+
+            if (options.bgcolor) {
+                var ctx = canvas.getContext('2d');
+                ctx.fillStyle = options.bgcolor;
+                ctx.fillRect(0, 0, canvas.width, canvas.height);
+            }
+
+            return canvas;
+        }
+    }
+
+    function cloneNode(node, filter, root) {
+        if (!root && filter && !filter(node)) return Promise.resolve();
+
+        return Promise.resolve(node)
+            .then(makeNodeCopy)
+            .then(function (clone) {
+                return cloneChildren(node, clone, filter);
+            })
+            .then(function (clone) {
+                return processClone(node, clone);
+            });
+
+        function makeNodeCopy(node) {
+            if (node instanceof HTMLCanvasElement) return util.makeImage(node.toDataURL());
+            return node.cloneNode(false);
+        }
+
+        function cloneChildren(original, clone, filter) {
+            var children = original.childNodes;
+            if (children.length === 0) return Promise.resolve(clone);
+
+            return cloneChildrenInOrder(clone, util.asArray(children), filter)
+                .then(function () {
+                    return clone;
+                });
+
+            function cloneChildrenInOrder(parent, children, filter) {
+                var done = Promise.resolve();
+                children.forEach(function (child) {
+                    done = done
+                        .then(function () {
+                            return cloneNode(child, filter);
+                        })
+                        .then(function (childClone) {
+                            if (childClone) parent.appendChild(childClone);
+                        });
+                });
+                return done;
+            }
+        }
+
+        function processClone(original, clone) {
+            if (!(clone instanceof Element)) return clone;
+
+            return Promise.resolve()
+                .then(cloneStyle)
+                .then(clonePseudoElements)
+                .then(copyUserInput)
+                .then(fixSvg)
+                .then(function () {
+                    return clone;
+                });
+
+            function cloneStyle() {
+                copyStyle(window.getComputedStyle(original), clone.style);
+
+                function copyStyle(source, target) {
+                    if (source.cssText) target.cssText = source.cssText;
+                    else copyProperties(source, target);
+
+                    function copyProperties(source, target) {
+                        util.asArray(source).forEach(function (name) {
+                            target.setProperty(
+                                name,
+                                source.getPropertyValue(name),
+                                source.getPropertyPriority(name)
+                            );
+                        });
+                    }
+                }
+            }
+
+            function clonePseudoElements() {
+                [':before', ':after'].forEach(function (element) {
+                    clonePseudoElement(element);
+                });
+
+                function clonePseudoElement(element) {
+                    var style = window.getComputedStyle(original, element);
+                    var content = style.getPropertyValue('content');
+
+                    if (content === '' || content === 'none') return;
+
+                    var className = util.uid();
+                    clone.className = clone.className + ' ' + className;
+                    var styleElement = document.createElement('style');
+                    styleElement.appendChild(formatPseudoElementStyle(className, element, style));
+                    clone.appendChild(styleElement);
+
+                    function formatPseudoElementStyle(className, element, style) {
+                        var selector = '.' + className + ':' + element;
+                        var cssText = style.cssText ? formatCssText(style) : formatCssProperties(style);
+                        return document.createTextNode(selector + '{' + cssText + '}');
+
+                        function formatCssText(style) {
+                            var content = style.getPropertyValue('content');
+                            return style.cssText + ' content: ' + content + ';';
+                        }
+
+                        function formatCssProperties(style) {
+
+                            return util.asArray(style)
+                                .map(formatProperty)
+                                .join('; ') + ';';
+
+                            function formatProperty(name) {
+                                return name + ': ' +
+                                    style.getPropertyValue(name) +
+                                    (style.getPropertyPriority(name) ? ' !important' : '');
+                            }
+                        }
+                    }
+                }
+            }
+
+            function copyUserInput() {
+                if (original instanceof HTMLTextAreaElement) clone.innerHTML = original.value;
+                if (original instanceof HTMLInputElement) clone.setAttribute("value", original.value);
+            }
+
+            function fixSvg() {
+                if (!(clone instanceof SVGElement)) return;
+                clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
+
+                if (!(clone instanceof SVGRectElement)) return;
+                ['width', 'height'].forEach(function (attribute) {
+                    var value = clone.getAttribute(attribute);
+                    if (!value) return;
+
+                    clone.style.setProperty(attribute, value);
+                });
+            }
+        }
+    }
+
+    function embedFonts(node) {
+        return fontFaces.resolveAll()
+            .then(function (cssText) {
+                var styleNode = document.createElement('style');
+                node.appendChild(styleNode);
+                styleNode.appendChild(document.createTextNode(cssText));
+                return node;
+            });
+    }
+
+    function inlineImages(node) {
+        return images.inlineAll(node)
+            .then(function () {
+                return node;
+            });
+    }
+
+    function makeSvgDataUri(node, width, height) {
+        return Promise.resolve(node)
+            .then(function (node) {
+                node.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
+                return new XMLSerializer().serializeToString(node);
+            })
+            .then(util.escapeXhtml)
+            .then(function (xhtml) {
+                return '<foreignObject x="0" y="0" width="100%" height="100%">' + xhtml + '</foreignObject>';
+            })
+            .then(function (foreignObject) {
+                return '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="' + height + '">' +
+                    foreignObject + '</svg>';
+            })
+            .then(function (svg) {
+                return 'data:image/svg+xml;charset=utf-8,' + svg;
+            });
+    }
+
+    function newUtil() {
+        return {
+            escape: escape,
+            parseExtension: parseExtension,
+            mimeType: mimeType,
+            dataAsUrl: dataAsUrl,
+            isDataUrl: isDataUrl,
+            canvasToBlob: canvasToBlob,
+            resolveUrl: resolveUrl,
+            getAndEncode: getAndEncode,
+            uid: uid(),
+            delay: delay,
+            asArray: asArray,
+            escapeXhtml: escapeXhtml,
+            makeImage: makeImage,
+            width: width,
+            height: height
+        };
+
+        function mimes() {
+            /*
+             * Only WOFF and EOT mime types for fonts are 'real'
+             * see http://www.iana.org/assignments/media-types/media-types.xhtml
+             */
+            var WOFF = 'application/font-woff';
+            var JPEG = 'image/jpeg';
+
+            return {
+                'woff': WOFF,
+                'woff2': WOFF,
+                'ttf': 'application/font-truetype',
+                'eot': 'application/vnd.ms-fontobject',
+                'png': 'image/png',
+                'jpg': JPEG,
+                'jpeg': JPEG,
+                'gif': 'image/gif',
+                'tiff': 'image/tiff',
+                'svg': 'image/svg+xml'
+            };
+        }
+
+        function parseExtension(url) {
+            var match = /\.([^\.\/]*?)$/g.exec(url);
+            if (match) return match[1];
+            else return '';
+        }
+
+        function mimeType(url) {
+            var extension = parseExtension(url).toLowerCase();
+            return mimes()[extension] || '';
+        }
+
+        function isDataUrl(url) {
+            return url.search(/^(data:)/) !== -1;
+        }
+
+        function toBlob(canvas) {
+            return new Promise(function (resolve) {
+                var binaryString = window.atob(canvas.toDataURL().split(',')[1]);
+                var length = binaryString.length;
+                var binaryArray = new Uint8Array(length);
+
+                for (var i = 0; i < length; i++)
+                    binaryArray[i] = binaryString.charCodeAt(i);
+
+                resolve(new Blob([binaryArray], {
+                    type: 'image/png'
+                }));
+            });
+        }
+
+        function canvasToBlob(canvas) {
+            if (canvas.toBlob)
+                return new Promise(function (resolve) {
+                    canvas.toBlob(resolve);
+                });
+
+            return toBlob(canvas);
+        }
+
+        function resolveUrl(url, baseUrl) {
+            var doc = document.implementation.createHTMLDocument();
+            var base = doc.createElement('base');
+            doc.head.appendChild(base);
+            var a = doc.createElement('a');
+            doc.body.appendChild(a);
+            base.href = baseUrl;
+            a.href = url;
+            return a.href;
+        }
+
+        function uid() {
+            var index = 0;
+
+            return function () {
+                return 'u' + fourRandomChars() + index++;
+
+                function fourRandomChars() {
+                    /* see http://stackoverflow.com/a/6248722/2519373 */
+                    return ('0000' + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4);
+                }
+            };
+        }
+
+        function makeImage(uri) {
+            return new Promise(function (resolve, reject) {
+                var image = new Image();
+                image.onload = function () {
+                    resolve(image);
+                };
+                image.onerror = reject;
+                image.src = uri;
+            });
+        }
+
+        function getAndEncode(url) {
+            var TIMEOUT = 30000;
+            if(domtoimage.impl.options.cacheBust) {
+                // Cache bypass so we dont have CORS issues with cached images
+                // Source: https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache
+                url += ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime();
+            }
+
+            return new Promise(function (resolve) {
+                var request = new XMLHttpRequest();
+
+                request.onreadystatechange = done;
+                request.ontimeout = timeout;
+                request.responseType = 'blob';
+                request.timeout = TIMEOUT;
+                request.open('GET', url, true);
+                request.send();
+
+                var placeholder;
+                if(domtoimage.impl.options.imagePlaceholder) {
+                    var split = domtoimage.impl.options.imagePlaceholder.split(/,/);
+                    if(split && split[1]) {
+                        placeholder = split[1];
+                    }
+                }
+
+                function done() {
+                    if (request.readyState !== 4) return;
+
+                    if (request.status !== 200) {
+                        if(placeholder) {
+                            resolve(placeholder);
+                        } else {
+                            fail('cannot fetch resource: ' + url + ', status: ' + request.status);
+                        }
+
+                        return;
+                    }
+
+                    var encoder = new FileReader();
+                    encoder.onloadend = function () {
+                        var content = encoder.result.split(/,/)[1];
+                        resolve(content);
+                    };
+                    encoder.readAsDataURL(request.response);
+                }
+
+                function timeout() {
+                    if(placeholder) {
+                        resolve(placeholder);
+                    } else {
+                        fail('timeout of ' + TIMEOUT + 'ms occured while fetching resource: ' + url);
+                    }
+                }
+
+                function fail(message) {
+                    console.error(message);
+                    resolve('');
+                }
+            });
+        }
+
+        function dataAsUrl(content, type) {
+            return 'data:' + type + ';base64,' + content;
+        }
+
+        function escape(string) {
+            return string.replace(/([.*+?^${}()|\[\]\/\\])/g, '\\$1');
+        }
+
+        function delay(ms) {
+            return function (arg) {
+                return new Promise(function (resolve) {
+                    setTimeout(function () {
+                        resolve(arg);
+                    }, ms);
+                });
+            };
+        }
+
+        function asArray(arrayLike) {
+            var array = [];
+            var length = arrayLike.length;
+            for (var i = 0; i < length; i++) array.push(arrayLike[i]);
+            return array;
+        }
+
+        function escapeXhtml(string) {
+            return string.replace(/#/g, '%23').replace(/\n/g, '%0A');
+        }
+
+        function width(node) {
+            var leftBorder = px(node, 'border-left-width');
+            var rightBorder = px(node, 'border-right-width');
+            return node.scrollWidth + leftBorder + rightBorder;
+        }
+
+        function height(node) {
+            var topBorder = px(node, 'border-top-width');
+            var bottomBorder = px(node, 'border-bottom-width');
+            return node.scrollHeight + topBorder + bottomBorder;
+        }
+
+        function px(node, styleProperty) {
+            var value = window.getComputedStyle(node).getPropertyValue(styleProperty);
+            return parseFloat(value.replace('px', ''));
+        }
+    }
+
+    function newInliner() {
+        var URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/g;
+
+        return {
+            inlineAll: inlineAll,
+            shouldProcess: shouldProcess,
+            impl: {
+                readUrls: readUrls,
+                inline: inline
+            }
+        };
+
+        function shouldProcess(string) {
+            return string.search(URL_REGEX) !== -1;
+        }
+
+        function readUrls(string) {
+            var result = [];
+            var match;
+            while ((match = URL_REGEX.exec(string)) !== null) {
+                result.push(match[1]);
+            }
+            return result.filter(function (url) {
+                return !util.isDataUrl(url);
+            });
+        }
+
+        function inline(string, url, baseUrl, get) {
+            return Promise.resolve(url)
+                .then(function (url) {
+                    return baseUrl ? util.resolveUrl(url, baseUrl) : url;
+                })
+                .then(get || util.getAndEncode)
+                .then(function (data) {
+                    return util.dataAsUrl(data, util.mimeType(url));
+                })
+                .then(function (dataUrl) {
+                    return string.replace(urlAsRegex(url), '$1' + dataUrl + '$3');
+                });
+
+            function urlAsRegex(url) {
+                return new RegExp('(url\\([\'"]?)(' + util.escape(url) + ')([\'"]?\\))', 'g');
+            }
+        }
+
+        function inlineAll(string, baseUrl, get) {
+            if (nothingToInline()) return Promise.resolve(string);
+
+            return Promise.resolve(string)
+                .then(readUrls)
+                .then(function (urls) {
+                    var done = Promise.resolve(string);
+                    urls.forEach(function (url) {
+                        done = done.then(function (string) {
+                            return inline(string, url, baseUrl, get);
+                        });
+                    });
+                    return done;
+                });
+
+            function nothingToInline() {
+                return !shouldProcess(string);
+            }
+        }
+    }
+
+    function newFontFaces() {
+        return {
+            resolveAll: resolveAll,
+            impl: {
+                readAll: readAll
+            }
+        };
+
+        function resolveAll() {
+            return readAll(document)
+                .then(function (webFonts) {
+                    return Promise.all(
+                        webFonts.map(function (webFont) {
+                            return webFont.resolve();
+                        })
+                    );
+                })
+                .then(function (cssStrings) {
+                    return cssStrings.join('\n');
+                });
+        }
+
+        function readAll() {
+            return Promise.resolve(util.asArray(document.styleSheets))
+                .then(getCssRules)
+                .then(selectWebFontRules)
+                .then(function (rules) {
+                    return rules.map(newWebFont);
+                });
+
+            function selectWebFontRules(cssRules) {
+                return cssRules
+                    .filter(function (rule) {
+                        return rule.type === CSSRule.FONT_FACE_RULE;
+                    })
+                    .filter(function (rule) {
+                        return inliner.shouldProcess(rule.style.getPropertyValue('src'));
+                    });
+            }
+
+            function getCssRules(styleSheets) {
+                var cssRules = [];
+                styleSheets.forEach(function (sheet) {
+                    try {
+                        util.asArray(sheet.cssRules || []).forEach(cssRules.push.bind(cssRules));
+                    } catch (e) {
+                        console.log('Error while reading CSS rules from ' + sheet.href, e.toString());
+                    }
+                });
+                return cssRules;
+            }
+
+            function newWebFont(webFontRule) {
+                return {
+                    resolve: function resolve() {
+                        var baseUrl = (webFontRule.parentStyleSheet || {}).href;
+                        return inliner.inlineAll(webFontRule.cssText, baseUrl);
+                    },
+                    src: function () {
+                        return webFontRule.style.getPropertyValue('src');
+                    }
+                };
+            }
+        }
+    }
+
+    function newImages() {
+        return {
+            inlineAll: inlineAll,
+            impl: {
+                newImage: newImage
+            }
+        };
+
+        function newImage(element) {
+            return {
+                inline: inline
+            };
+
+            function inline(get) {
+                if (util.isDataUrl(element.src)) return Promise.resolve();
+
+                return Promise.resolve(element.src)
+                    .then(get || util.getAndEncode)
+                    .then(function (data) {
+                        return util.dataAsUrl(data, util.mimeType(element.src));
+                    })
+                    .then(function (dataUrl) {
+                        return new Promise(function (resolve, reject) {
+                            element.onload = resolve;
+                            element.onerror = reject;
+                            element.src = dataUrl;
+                        });
+                    });
+            }
+        }
+
+        function inlineAll(node) {
+            if (!(node instanceof Element)) return Promise.resolve(node);
+
+            return inlineBackground(node)
+                .then(function () {
+                    if (node instanceof HTMLImageElement)
+                        return newImage(node).inline();
+                    else
+                        return Promise.all(
+                            util.asArray(node.childNodes).map(function (child) {
+                                return inlineAll(child);
+                            })
+                        );
+                });
+
+            function inlineBackground(node) {
+                var background = node.style.getPropertyValue('background');
+
+                if (!background) return Promise.resolve(node);
+
+                return inliner.inlineAll(background)
+                    .then(function (inlined) {
+                        node.style.setProperty(
+                            'background',
+                            inlined,
+                            node.style.getPropertyPriority('background')
+                        );
+                    })
+                    .then(function () {
+                        return node;
+                    });
+            }
+        }
+    }
+})(this);
index 12a78e18499c99047fbe2c110e9a3e3e71919ceb..f9d91562d6e814d29a3ef6190704d1d59042fa1c 100644 (file)
@@ -247,8 +247,7 @@ IstinaEditor.highlightForQAnsel = function(txt)
                "barrier",
                "hvar",
                "rand",
-               "born",
-               "bsample"
+               "born"
        ];
                
        for (var i = 0; i < keywords.length; i++)
@@ -274,6 +273,20 @@ IstinaEditor.highlightForQAnsel = function(txt)
                });
        }
        txt = txt.replaceAll
+       (
+               /(rx|ry|rz)[(].*?[)]/g,
+               x =>
+               {
+                       var lhs = x.split("(")[0];
+                       var rhs = x.split("(")[1].split(")")[0];
+                       x = "<span class='istina-keyword'>" + lhs + "</span>";
+                       x += "<span class='istina-bracket'>(</span>";
+                       x += "<span class='istina-number'>" + rhs + "</span>";
+                       x += "<span class='istina-bracket'>)</span>";
+                       return x;
+               }
+       );
+       txt = txt.replaceAll
        (
                /[/][/].*?([<]div|[<]br|$)/g,
                x =>
@@ -401,7 +414,7 @@ IstinaEditor.init = function()
                        txt = "";
                        for (var i = 0; i < linecount; i++)
                        {
-                               txt += (i + 1) + "<br />";
+                               txt += "<div>" + (i + 1) + "</div>";
                        }
                        $(".istina-linecounter")[0].innerHTML = txt;
                }
diff --git a/src/imports/measure.png b/src/imports/measure.png
new file mode 100644 (file)
index 0000000..d44c75c
Binary files /dev/null and b/src/imports/measure.png differ
diff --git a/src/index.html b/src/index.html
new file mode 100644 (file)
index 0000000..2584723
--- /dev/null
@@ -0,0 +1,1134 @@
+<head>
+       <link href="imports/istina-editor.css" rel="stylesheet" />
+       <link href="imports/micromodal.css" rel="stylesheet" />
+       <script src="imports/estouls-api.js"></script>
+       <script src="imports/istina-editor.js"></script>
+       <script src="imports/chart.js"></script>
+       <script src="imports/micromodal.js"></script>
+       <script src="imports/dom-to-image.js"></script>
+       <style>
+               body { background-color: #1f1f1f; user-select: none; }
+               [class*=program] td
+               {
+                       font-family: 'Courier New', Courier, monospace;
+                       text-align: center;
+                       user-select: none;
+                       background-color: #353535;
+                       color: #9cdcfe;
+                       border-color: grey;
+                       font-weight: bold;
+                       border-style: solid;
+                       border-collapse: collapse;
+               }
+               .program-track
+               {
+                       table-layout: fixed;
+               }
+               [pidx*=',']:hover,.program-expand td:hover
+               {
+                       background-color: rgb(75, 0, 0);
+                       cursor: pointer;
+               }
+               /*.program-instr td:hover
+               {
+                       background-color: rgb(75, 0, 0);
+               }*/
+               textarea
+               {
+                       background-color: #353535;
+                       color: #dcdcaa;
+                       font-family: 'Courier New', Courier, monospace;
+                       width: 100%;
+                       height: 220px;
+               }
+               /*
+               .optionspanel
+               {
+                       text-align: right;
+                       width: 100%;
+               }
+               .option
+               {
+                       font-size: 24px;
+                       font-weight: bold;
+                       font-family: monospace;
+                       cursor: pointer;
+                       background-color: rgba(0,0,0,0);
+                       padding: 0;
+                       margin: 0;
+                       border: 0;
+                       display: inline-block;
+               }
+               #run { color: green; }
+               #setup { font-size: 28px; color: grey; }
+               #save,#help { font-size: 20px; }
+               #load,#clear,#apk { font-size: 20px; }
+               #apkwrapper { display: none; }
+               */
+
+               .top-button-row td:hover
+               {
+                       background-color: gray;
+                       cursor: pointer;
+               }
+
+               @keyframes animation-reveal
+               {
+                       from {
+                               clip-path: polygon(0% 0%, 0% 0%, 100% 0%, 100% 0%);
+                       }
+                       to {
+                               clip-path: polygon(0% 0%, 0% 100%, 100% 100%, 100% 0%);
+                       }
+               }
+       </style>
+</head>
+<body>
+       <input type="file" id="file-input" style="display: none;" />
+       <div class="modal micromodal-slide" id="modal-main" aria-hidden="true">
+               <div class="modal__overlay" tabindex="-1" data-micromodal-close oncontextmenu="return false;">
+                       <div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-main-title" style="width: 640px;">
+                               <header class="modal__header">
+                                       <h2 class="modal__title" id="modal-main-title"></h2>
+                                       <button class="modal__close" aria-label="Close modal" data-micromodal-close style="cursor: pointer;"></button>
+                               </header>
+                               <main class="modal__content" id="modal-main-content"></main>
+                               <footer class="modal__footer">
+                                       <button class="modal__btn modal__btn-primary" id="modal-main-confirm">Confirm</button>&nbsp;
+                                       <button class="modal__btn" id="modal-main-cancel" onclick="closeModal();">Cancel</button>
+                               </footer>
+                       </div>
+               </div>
+       </div>
+       <table class="main-table" border="1" style="width: 100%;">
+               <tr>
+                       <td colspan="2">
+                               <table style="width: 100%; text-align: center;" class="top-button-row">
+                                       <tr>
+                                               <td onclick="menu('open');">๐Ÿ“‚</td>
+                                               <td onclick="menu('clear');">๐Ÿงน</td>
+                                               <td onclick="menu('save');">๐Ÿ’พ</td>
+                                               <td style="color: green;" id="run">โ–ถ</td>
+                                               <td onclick="menu('download');">๐Ÿ“ฅ</td>
+                                               <td onclick="menu('screenshot');">๐Ÿ“ท</td>
+                                               <td>โ“</td>
+                                               <td onclick="menu('settings');" style="color: lightgray">โš™</td>
+                                       </tr>
+                               </table>
+                       </td>
+               </tr>
+               <tr>
+                       <td colspan="2">
+                               <div style="overflow-y: auto; overflow-x: auto; height: 256px; width: 0px;" oncontextmenu="return false;" class="program-track-container">
+                                       <table border=1 class="program-track"></table>
+                                       <table border=1 class="program-expand">
+                                               <tr>
+                                                       <td style="width: 64px; height: 64px;" onclick="ProgramTrack.adjust(-1);">โˆ’</td>
+                                                       <td style="width: 64px; height: 64px;" onclick="ProgramTrack.adjust(1);">๏ผ‹</td>
+                                               </tr>
+                                       </table>
+                               </div>
+                       </td>
+               </tr>
+               <tr>
+                       <td style="width: 50%; height: 100%;">
+                               <istina-editor style="height: 100%;"></istina-editor>
+                       </td>
+                       <td style="width: 50%;">
+                               <canvas id="resultsChart"></canvas>
+                       </td>
+               </tr>
+       </table>
+       <table border=1 class="program-instr" style="display: none;" cellspacing="0" cellpadding="0"></table>
+       <table border=1 class="program-drag" style="visibility: hidden;"></table>
+       <script>
+               $ = x => { return document.querySelectorAll(x); };
+               IstinaEditor.init();
+               function overline(instr, pos, compare)
+               {
+                       var r;
+                       switch (instr)
+                       {
+                               case "CX":
+                                       switch (pos)
+                                       {
+                                               case "10": r = "<span style='text-decoration:overline;'>C</span>X"; break;
+                                               case "01": r = "C<span style='text-decoration:overline;'>X</span>"; break;
+                                       }
+                               break;
+                               case "SW":
+                                       switch (pos)
+                                       {
+                                               case "10": r = "<span style='text-decoration:overline;'>S</span>W"; break;
+                                               case "01": r = "S<span style='text-decoration:overline;'>W</span>"; break;
+                                       }
+                               break;
+                               case "CCX":
+                                       switch (pos)
+                                       {
+                                               case "100": r = "<span style='text-decoration:overline;'>C</span>CX"; break;
+                                               case "010": r = "C<span style='text-decoration:overline;'>C</span>X"; break;
+                                               case "001": r = "CC<span style='text-decoration:overline;'>X</span>"; break;
+                                       }
+                               break;
+                               case "CSW":
+                                       switch (pos)
+                                       {
+                                               case "100": r = "<span style='text-decoration:overline;'>C</span>SW"; break;
+                                               case "010": r = "C<span style='text-decoration:overline;'>S</span>W"; break;
+                                               case "001": r = "CS<span style='text-decoration:overline;'>W</span>"; break;
+                                       }
+                               break;
+                       }
+                       if (compare == undefined) return r;
+                       return compare.toString().replace(/[\"]/g, "'").trim() == r;
+               }
+
+               var INSTRUCTIONS =
+               [
+                       "X", "Pauli-X",
+                       "Y", "Pauli-Y",
+                       "Z", "Pauli-Z",
+                       "H", "Hadamard",
+                       "S", "Phase-S",
+                       "T", "Phase-T",
+                       "S<sup>&dagger;</sup>", "Phase-S Hermitian Transpose",
+                       "T<sup>&dagger;</sup>", "Phase-T Hermitian Transpose",
+                       "CX", "Controlled-NOT",
+                       "CCX", "Toffoli",
+                       "SW", "Swap",
+                       "CSW", "Fredkin",
+                       "RX", "Rotation-X",
+                       "RY", "Rotation-Y",
+                       "RZ", "Rotation-Z",
+                       "|0โŸฉ", "Qubit Set",
+                       "|1โŸฉ", "Qubit Reset",
+                       "<span class='measure'>๐Ÿ”</span>", "Measure",
+                       "=1", "Conditional Set",
+                       "=0", "Conditional Reset"
+               ]; 
+               var ProgramTrack =
+               {
+                       SCALE: 64,
+                       STATE: [ [], [] ],
+                       DRAG: "",
+                       DRAGMODE: false,
+                       DRAGPOS: [0, 0],
+                       DRAGELEM: undefined,
+                       DRAGHOLD: undefined,
+                       init: () =>
+                       {
+                               ProgramTrack.STATE = [ [], [] ];
+                               ProgramTrack.draw();
+                       },
+                       adjust: x =>
+                       {
+                               if (x == 1)
+                               {
+                                       if (ProgramTrack.STATE.length < 15)
+                                       {
+                                               var len = ProgramTrack.STATE[0].length;
+                                               ProgramTrack.STATE[ProgramTrack.STATE.length] = new Array(len);
+                                               for (var i = 0; i < len; i++)
+                                               {
+                                                       ProgramTrack.STATE[ProgramTrack.STATE.length - 1][i] = ProgramTrack.STATE[ProgramTrack.STATE.length - 2][i];
+                                                       ProgramTrack.STATE[ProgramTrack.STATE.length - 2][i] = "";
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       if (ProgramTrack.STATE.length > 2)
+                                       {
+                                               var len = ProgramTrack.STATE[0].length;
+                                               for (var i = 0; i < len; i++)
+                                               {
+                                                       ProgramTrack.STATE[ProgramTrack.STATE.length - 2][i] = ProgramTrack.STATE[ProgramTrack.STATE.length - 1][i];
+                                               }
+                                               ProgramTrack.STATE.length -= 1;
+                                       }
+                               }
+                               trackerCollapse(null);
+                               ProgramTrack.draw();
+                               $(".program-track-container")[0].scrollTop = 999999;
+                       },
+                       decode: () =>
+                       {
+                               var width = 0;
+                               for (var i = 0; i < ProgramTrack.STATE.length - 1; i++)
+                               {
+                                       if (ProgramTrack.STATE[i].length > width)
+                                       {
+                                               width = ProgramTrack.STATE[i].length;
+                                       }
+                               }
+                               var ret = "";
+                               var maxQ = -1;
+                               var maxC = -1;
+                               for (var i = 0; i < width; i++)
+                               {
+                                       var ifblk = ProgramTrack.STATE[ProgramTrack.STATE.length - 1][i].trim();
+                                       if (ifblk.length > 0)
+                                       {
+                                               var a = ifblk.split("<sub>")[0].includes("0") ? 0 : 1;
+                                               var b = parseInt(ifblk.split("<sub>")[1].split("<")[0]) - 1;
+                                               ifblk = "if(c[" + b + "]==" + a + ") ";
+                                               if (b > maxC) maxC = b;
+                                       }
+                                       var cx = [0, -1, -1];
+                                       var sw = [0, -1, -1];
+                                       var ccx = [0, -1, -1, -1];
+                                       var csw = [0, -1, -1, -1];
+                                       for (var j = 0; j < ProgramTrack.STATE.length - 1; j++)
+                                       {
+                                               var instr = ProgramTrack.STATE[j][i].trim();
+                                               if (instr.length == 0) continue;
+                                               if (j > maxQ) maxQ = j;
+                                               switch (instr)
+                                               {
+                                                       case "X": ret += ifblk + "x q[" + j + "];\n"; break;
+                                                       case "Y": ret += ifblk + "y q[" + j + "];\n"; break;
+                                                       case "Z": ret += ifblk + "z q[" + j + "];\n"; break;
+                                                       case "H": ret += ifblk + "h q[" + j + "];\n"; break;
+                                                       case "S": ret += ifblk + "s q[" + j + "];\n"; break;
+                                                       case "T": ret += ifblk + "t q[" + j + "];\n"; break;
+                                                       case "S<sup>โ€ </sup>": ret += ifblk + "sdg q[" + j + "];\n"; break;
+                                                       case "T<sup>โ€ </sup>": ret += ifblk + "tdg q[" + j + "];\n"; break;
+                                                       case "|1โŸฉ": ret += ifblk + "set q[" + j + "];\n"; break;
+                                                       case "|0โŸฉ": ret += ifblk + "reset q[" + j + "];\n"; break;
+                                                       default:
+                                                               if (instr.includes("๐Ÿ”"))
+                                                               {
+                                                                       var num = parseInt(instr.split("<sub>")[1]);
+                                                                       if (num > maxC) maxC = num;
+                                                                       num -= 1;
+                                                                       ret += ifblk + "measure q[" + j + "] -> c[" + num + "];\n";
+                                                               }
+                                                               else if (instr.includes("RX") || instr.includes("RY") || instr.includes("RZ"))
+                                                               {
+                                                                       var tmpL = instr.replace("ฯ€", "pi");
+                                                                       var tmpR = tmpL.split("<span")[1].split("</span")[0].split(">")[1];
+                                                                       tmpL = tmpL.split("<")[0].toLowerCase();
+                                                                       ret += tmpL + "(" + tmpR + ") q[" + j + "];\n";
+                                                               }
+                                               }
+                                               if (overline("CX", "10", instr)) { if (cx[1] != -1) { cx[0] = 1; } cx[1] = j; }
+                                               if (overline("CX", "01", instr)) { if (cx[2] != -1) { cx[0] = 1; } cx[2] = j; }
+                                               if (overline("SW", "10", instr)) { if (sw[1] != -1) { sw[0] = 1; } sw[1] = j; }
+                                               if (overline("SW", "01", instr)) { if (sw[2] != -1) { sw[0] = 1; } sw[2] = j; }
+
+                                               if (overline("CCX", "100", instr)) { if (ccx[1] != -1) { ccx[0] = 1; } ccx[1] = j; }
+                                               if (overline("CCX", "010", instr)) { if (ccx[2] != -1) { ccx[0] = 1; } ccx[2] = j; }
+                                               if (overline("CCX", "001", instr)) { if (ccx[3] != -1) { ccx[0] = 1; } ccx[3] = j; }
+                                               if (overline("CSW", "100", instr)) { if (csw[1] != -1) { csw[0] = 1; } csw[1] = j; }
+                                               if (overline("CSW", "010", instr)) { if (csw[2] != -1) { csw[0] = 1; } csw[2] = j; }
+                                               if (overline("CSW", "001", instr)) { if (csw[3] != -1) { csw[0] = 1; } csw[3] = j; }
+                                       }
+
+                                       if (cx[0]) { ret = "Duplicate `CX` at column " + (i + 1) + "."; break; }
+                                       if (cx[1] != -1 && cx[2] != -1) { ret += ifblk + "cx q[" + cx[1] + "], q[" + cx[2] + "];\n"; }
+                                       else if (cx[1] != -1 || cx[2] != -1) { ret = "Incomplete `CX` at column " + (i + 1) + "."; break; }
+
+                                       if (sw[0]) { ret = "Duplicate `SW` at column " + (i + 1) + "."; break; }
+                                       if (sw[1] != -1 && sw[2] != -1) { ret += ifblk + "swap q[" + sw[1] + "], q[" + sw[2] + "];\n"; }
+                                       else if (sw[1] != -1 || sw[2] != -1) { ret = "Incomplete `SW` at column " + (i + 1) + "."; break; }
+
+                                       if (ccx[0]) { ret = "Duplicate `CCX` at column " + (i + 1) + "."; break; }
+                                       if (ccx[1] != -1 && ccx[2] != -1 && ccx[3] != -1) { ret += ifblk + "ccx q[" + ccx[1] + "], q[" + ccx[2] + "], q[" + ccx[3] + "];\n"; }
+                                       else if (ccx[1] != -1 || ccx[2] != -1 || ccx[3] != -1) { ret = "Incomplete `CCX` at column " + (i + 1) + "."; break; }
+
+                                       if (csw[0]) { ret = "Duplicate `CSW` at column " + (i + 1) + "."; break; }
+                                       if (csw[1] != -1 && csw[2] != -1 && csw[3] != -1) { ret += ifblk + "cswap q[" + csw[1] + "], q[" + csw[2] + "], q[" + csw[3] + "];\n"; }
+                                       else if (csw[1] != -1 || csw[2] != -1 || csw[3] != -1) { ret = "Incomplete `CSW` at column " + (i + 1) + "."; break; }
+                               }
+                               if (!ret.includes("Duplicate") && !ret.includes("Incomplete"))
+                               {
+                                       if (maxC >= 0)
+                                       {
+                                               ret = "creg c[" + (maxC + 1) + "];\n" + ret;
+                                       }
+                                       if (maxQ >= 0)
+                                       {
+                                               ret = "qreg q[" + (maxQ + 1) + "];\n" + ret;
+                                               if (maxC >= 0)
+                                               {
+                                                       ret += "sample c;\n";
+                                               }
+                                               else
+                                               {
+                                                       ret += "sample q;\n";
+                                               }
+                                       }
+                                       
+                               }
+                               return ret;
+                       },
+                       drag: (e, erase, evt) =>
+                       {
+                               ProgramTrack.DRAGMODE = erase;
+                               $(".program-instr")[0].style.display = "none";
+                               if (e.innerHTML.includes("Q") || e.innerHTML.includes("IF") || e.innerHTML.includes("..."))
+                               {
+                                       return;
+                               }
+                               if (evt.button == 2) 
+                               {
+                                       if (e.getAttribute("pidx") != null)
+                                       {
+                                               var s = e.getAttribute("pidx").split(",");
+                                               s[0] = parseInt(s[0]);
+                                               s[1] = parseInt(s[1]);
+                                               var state = ProgramTrack.STATE[s[0]][s[1]];
+                                               if (overline("CX", "10", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CX", "01");
+                                               if (overline("CX", "01", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CX", "10");
+                                               if (overline("SW", "10", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("SW", "01");
+                                               if (overline("SW", "01", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("SW", "10");
+                                               if (overline("CCX", "100", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CCX", "010");
+                                               if (overline("CCX", "010", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CCX", "001");
+                                               if (overline("CCX", "001", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CCX", "100");
+                                               if (overline("CSW", "100", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CSW", "010");
+                                               if (overline("CSW", "010", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CSW", "001");
+                                               if (overline("CSW", "001", state)) ProgramTrack.STATE[s[0]][s[1]] = overline("CSW", "100");
+                                               if (state.includes("RX") || state.includes("RY") || state.includes("RZ"))
+                                               {
+                                                       ProgramTrack.DRAGHOLD = [s[0], s[1]];
+                                                       getRotationAngle(state.split("<")[0]);
+                                               }
+                                               var instr = ProgramTrack.STATE[s[0]][s[1]].split("<sub>")[0];
+                                               if (instr.includes("=1") || instr.includes("=0") || instr.includes("๐Ÿ”"))
+                                               {
+                                                       var a = parseInt(ProgramTrack.STATE[s[0]][s[1]].split("<sub>")[1]) - 1;
+                                                       a = (a + 1) % (ProgramTrack.STATE.length - 1);
+                                                       ProgramTrack.STATE[s[0]][s[1]] = instr + "<sub>" + (a + 1) + "</sub>";
+                                               }
+                                               ProgramTrack.draw();
+                                       }
+                                       evt.preventDefault();
+                                       return false;
+                               }
+                               var px = ProgramTrack.SCALE + "px";
+                               var tr = "<tr style='height: " + px + "'>";
+                               var td = "<td style='width: " + px + "; height: " + px + "; cursor: grab;'>";
+                               if (ProgramTrack.DRAG == "" && e.innerHTML.trim().length > 0)
+                               {
+                                       var h = e.innerHTML.replace(/[<].*?[>]/g, "");
+                                       h = e.innerHTML;
+                                       ProgramTrack.DRAG = h;
+                                       ProgramTrack.DRAGPOS = [evt.clientX, evt.clientY];
+                                       $(".program-drag")[0].innerHTML = tr + td + h + "</tr></td>";                           
+                                       $(".program-drag")[0].style.visibility = "";
+                               }
+                               if (erase)
+                               {
+                                       var s = e.getAttribute("pidx").split(",");
+                                       s[0] = parseInt(s[0]);
+                                       s[1] = parseInt(s[1]);
+                                       ProgramTrack.STATE[s[0]][s[1]] = "";
+                                       e.innerHTML = "";
+                               }
+                       },
+                       draw: () =>
+                       {
+                               var drag_event = "onmousedown='ProgramTrack.drag(this, true, event);'";
+                               var px = ProgramTrack.SCALE + "px";
+                               var tr = "<tr style='height: " + px + "'>";
+                               var td = "<td style='min-width: " + px + "; width: " + px + "; height: " + px + ";' qubit='1'";
+                               td += ">";
+                               var html = "";
+                               for (var i = 0; i < ProgramTrack.STATE.length; i++)
+                               {
+                                       if (i == ProgramTrack.STATE.length - 1)
+                                       {
+                                               td = td.replace("qubit='1'", "qubit='0'");
+                                               html += tr + td.replace(drag_event, "");
+                                               html += "IF</td>";
+                                       }
+                                       else
+                                       {
+                                               td = td.replace("qubit='0'", "qubit='1'");
+                                               html += tr + td.replace(drag_event, "");
+                                               html += "Q<sub>" + (i + 1) + "</sub></td>";
+                                       }
+                                       td = td.replace(">", drag_event + ">");
+                                       for (var j = 0; j < ProgramTrack.STATE[i].length; j++)
+                                       {
+                                               var tdi = td.replace("<td", "<td pidx='" + i + "," + j + "' ");
+                                               html += tdi + ProgramTrack.STATE[i][j] + "</td>";
+                                       }
+                                       html += td.replace("<td", "<td pidx='" + i + "," + ProgramTrack.STATE[i].length + "' ").replace(drag_event, "") + "...</td></tr>";
+                               }
+                               $(".program-track")[0].innerHTML = html;
+                               
+                               td = "<td style='width: " + px + "; height: " + px + "; cursor: grab;'";
+                               td += " oncontextmenu='return false;'";
+                               td += " " + drag_event.replace("true", "false");
+                               var itr = 0;
+                               html = tr;
+                               for (var i = 0; i < INSTRUCTIONS.length; i += 2)
+                               {
+                                       html += td + "title='" + INSTRUCTIONS[i + 1] + "'" + ">" + INSTRUCTIONS[i] + "</td>";
+                                       itr++;
+                                       if (itr > 4 && i != INSTRUCTIONS.length - 2)
+                                       {
+                                               html += "</tr>" + tr;
+                                               itr = 0;
+                                       }
+                               }
+                               html += "</tr>";
+                               $(".program-instr")[0].innerHTML = html;
+                               IstinaEditor.setText(ProgramTrack.decode());
+                       }
+               };
+               
+               window.onload = function()
+               {
+                       ProgramTrack.draw();
+                       //$(".program-track")[0].parentNode.style.height = Math.round($(".program-instr")[0].getBoundingClientRect().height) + "px";
+                       $(".program-track")[0].parentNode.style.width = "100%";
+                       //Math.round($(".program-instr")[0].getBoundingClientRect().width) + "px";
+                       $(".program-track-container")[0].style.height = (ProgramTrack.SCALE*4 + 10) + "px"
+                       $(".program-track-container")[0].style.width = $(".program-track-container")[0].parentNode.getBoundingClientRect().width + "px"
+                       $(".program-track-container")[0].scrollTo(0, 0);
+               };
+
+               document.body.addEventListener("keydown", e =>
+               {
+                       modalFixes();
+               });
+               document.body.addEventListener("mousemove", e =>
+               {
+                       modalFixes();
+                       var x = $(".program-drag")[0];
+                       var w = parseFloat(getComputedStyle(x).width.replace("px", "")) / 2;
+                       var h = parseFloat(getComputedStyle(x).height.replace("px", "")) / 2;
+                       x.style.position = "absolute";
+                       x.style.left = (e.clientX - w) + "px";
+                       x.style.top = (e.clientY - h) + "px";
+               });
+               document.body.addEventListener("mouseup", e =>
+               {
+                       if ($(".program-drag")[0].style.visibility == "hidden")
+                       {
+                               var trackerElem = document.elementFromPoint(e.clientX, e.clientY);
+                               var str = trackerElem.getAttribute("pidx");
+                               if (str) {} else {str = ""};
+                               if (str.includes(",") && (trackerElem.innerHTML == "..." || trackerElem.innerHTML == ""))
+                               {
+                                       ProgramTrack.DRAGELEM = trackerElem;
+                                       $(".program-instr")[0].style.display = "none";
+                                       $(".program-instr")[0].className = $(".program-instr")[0].className.replace("menu-hidden", "").trim();
+                                       $(".program-instr")[0].className += " menu-reveal";
+                                       $(".program-instr")[0].style.position = "fixed";
+                                       $(".program-instr")[0].style.top = e.clientY + "px";
+                                       $(".program-instr")[0].style.left = e.clientX + "px";
+                                       $(".program-instr")[0].style.animation = "animation-reveal 0.2s forwards";
+                                       $(".program-instr")[0].style.display = "";
+                               }
+                               else
+                               {
+                                       $(".program-instr")[0].style.display = "none";
+                               }
+                               return;
+                       }
+                       $(".program-instr")[0].style.display = "none";
+                       ProgramTrack.draw();
+                       $(".program-drag")[0].style.visibility = "hidden";
+                       ProgramTrack.DRAG = "";
+
+                       var elem = document.elementFromPoint(e.clientX, e.clientY);
+                       var dist = Math.sqrt(Math.pow(ProgramTrack.DRAGPOS[0] - e.clientX, 2) + Math.pow(ProgramTrack.DRAGPOS[1] - e.clientY, 2));
+                       if (dist <= 16 && !ProgramTrack.DRAGMODE) elem = ProgramTrack.DRAGELEM;
+                       var attr = elem.getAttribute("pidx");
+                       if (elem.innerHTML.trim() != "...") trackerCollapse(attr);
+                       if (attr == null) return;
+                       attr = attr.split(",");
+                       attr[0] = parseInt(attr[0]);
+                       attr[1] = parseInt(attr[1]);
+                       var html = $(".program-drag td")[0].innerHTML;
+                       var qubit = parseInt(elem.getAttribute("qubit"));
+                       switch (html.split("<")[0])
+                       {
+                               case "=1": case "=0":
+                                       if (qubit) return;
+                               break;
+                               default:
+                                       if (!qubit) return;
+                       }
+                       var f = [0, 0, 0];
+                       switch (html)
+                       {
+                               case "CX":
+                                       if (ProgramTrack.STATE[attr[0]].length > attr[1])
+                                       {
+                                               for (var i = 0; i < ProgramTrack.STATE.length; i++)
+                                               {
+                                                       if (i != attr[0])
+                                                       {
+                                                               if (overline("CX", "10", ProgramTrack.STATE[i][attr[1]])) f[0] = 1;
+                                                               if (overline("CX", "01", ProgramTrack.STATE[i][attr[1]])) f[1] = 1;
+                                                       }
+                                               }
+                                       }
+                                       if (!f[0]) html = overline("CX", "10");
+                                       else html = overline("CX", "01");
+                               break;
+                               case "SW":
+                                       if (ProgramTrack.STATE[attr[0]].length > attr[1])
+                                       {
+                                               for (var i = 0; i < ProgramTrack.STATE.length; i++)
+                                               {
+                                                       if (i != attr[0])
+                                                       {
+                                                               if (overline("SW", "10", ProgramTrack.STATE[i][attr[1]])) f[0] = 1;
+                                                               if (overline("SW", "01", ProgramTrack.STATE[i][attr[1]])) f[1] = 1;
+                                                       }
+                                               }
+                                       }
+                                       if (!f[0]) html = overline("SW", "10");
+                                       else html = overline("SW", "01");
+                               break;
+                               case "CCX":
+                                       if (ProgramTrack.STATE[attr[0]].length > attr[1])
+                                       {
+                                               for (var i = 0; i < ProgramTrack.STATE.length; i++)
+                                               {
+                                                       if (i != attr[0])
+                                                       {
+                                                               if (overline("CCX", "100", ProgramTrack.STATE[i][attr[1]])) f[0] = 1;
+                                                               if (overline("CCX", "010", ProgramTrack.STATE[i][attr[1]])) f[1] = 1;
+                                                               if (overline("CCX", "001", ProgramTrack.STATE[i][attr[1]])) f[2] = 1;
+                                                       }
+                                               }
+                                       }
+                                       if (!f[0]) html = overline("CCX", "100");
+                                       else if (!f[1]) html = overline("CCX", "010");
+                                       else html = overline("CCX", "001");
+                               break;
+                               case "CSW":
+                                       if (ProgramTrack.STATE[attr[0]].length > attr[1])
+                                       {
+                                               for (var i = 0; i < ProgramTrack.STATE.length; i++)
+                                               {
+                                                       if (i != attr[0])
+                                                       {
+                                                               if (overline("CSW", "100", ProgramTrack.STATE[i][attr[1]])) f[0] = 1;
+                                                               if (overline("CSW", "010", ProgramTrack.STATE[i][attr[1]])) f[1] = 1;
+                                                               if (overline("CSW", "001", ProgramTrack.STATE[i][attr[1]])) f[2] = 1;
+                                                       }
+                                               }
+                                       }
+                                       if (!f[0]) html = overline("CSW", "100");
+                                       else if (!f[1]) html = overline("CSW", "010");
+                                       else html = overline("CSW", "001");
+                               break;
+                               case "=1": html = "=1<sub>1</sub>"; break;
+                               case "=0": html = "=0<sub>1</sub>"; break;
+                               case "RX":
+                               case "RY":
+                               case "RZ":
+                                       html += "<br /><span style='font-size: 12px'>0</span>";
+                                       ProgramTrack.DRAGHOLD = [attr[0], attr[1]];
+                                       getRotationAngle(html.split("<")[0]);
+                               break;
+                               default:
+                                       if (html.includes("๐Ÿ”") && !html.includes("<sub>"))
+                                       {
+                                               html += "<sub>1</sub>";
+                                       }
+                       }
+
+                       ProgramTrack.STATE[attr[0]][attr[1]] = html;
+                       var mx = -1;
+                       for (var i = 0; i < ProgramTrack.STATE.length; i++)
+                       {
+                               if (ProgramTrack.STATE[i].length > mx)
+                               {
+                                       mx = ProgramTrack.STATE[i].length;
+                               }
+                       }
+                       for (var i = 0; i < ProgramTrack.STATE.length; i++)
+                       {
+                               while (ProgramTrack.STATE[i].length < mx)
+                               {
+                                       ProgramTrack.STATE[i][ProgramTrack.STATE[i].length] = "";
+                               }
+                       }
+                       ProgramTrack.draw();
+                       if (elem.innerHTML.trim() == "...")
+                       {
+                               $(".program-track-container")[0].scrollLeft = 999999;
+                       }
+               });
+               //Chart
+               var resultsChart = new Chart(document.getElementById("resultsChart"),
+               {
+                       type: 'bar',
+                       data:
+                       {
+                               labels: [ ],
+                               datasets:
+                               [{
+                                       label: '',
+                                       data: [],
+                                       borderWidth: 1
+                               }]
+                       },
+                       options:
+                       {
+                               scales: { y: { beginAtZero: true } },
+                               plugins: { legend: { display: false } },
+
+                               animation:
+                               {
+                                       onComplete: function()
+                                       {
+                                               console.log('Chart update complete');
+                       }
+                       }
+                       }
+               });
+               function displayResults(results)
+               {
+                       var bitCount = Math.ceil(Math.log2(results.length));
+                       resultsChart.data.datasets[0].data = results;
+                       resultsChart.data.labels = [];
+                       for (var i = 0; i < results.length; i++)
+                       {
+                               resultsChart.data.labels[resultsChart.data.labels.length] = i.toString(2).padStart(bitCount, "0");
+                       }
+                       resultsChart.update();
+               }
+               function downloadResults()
+               {
+                       var csv = "";
+                       for (var i = 0; i < resultsChart.data.labels.length; i++)
+                       {
+                               csv += resultsChart.data.labels[i] + ",";
+                               csv += resultsChart.data.datasets[0].data[i] + "\r\n";
+                       }
+                       if (csv.length == 0)
+                       {
+                               errorModal("No data to download.");
+                               return;
+                       }
+                       var a = document.createElement("a");
+                       a.href = "data:text/plain," + escape(csv);
+                       a.download = "data.csv";
+                       document.body.appendChild(a);
+                       a.click();
+                       a.remove();
+               }
+
+               $("#run")[0].addEventListener("click", () =>
+               {
+                       if (ProgramTrack.STATE[0].length == 0)
+                       {
+                               errorModal("You must place at least one logic gate.");
+                               return;
+                       }
+                       createBlocker();
+                       var key = localStorage.getItem("apikey");
+                       key = key ? key : "";
+                       if (key.split(":").length != 3)
+                       {
+                               $("#blocker-div")[0].remove();
+                               $("#blocker-img")[0].remove();
+                               errorModal("Please click the gear icon and input your API key.");
+                               return;
+                       }
+                       EstoulsAPI.username = key.split(":")[0];
+                       EstoulsAPI.apikey = key.split(":")[1];
+                       EstoulsAPI.endpoint = "https://" + key.split(":")[2];
+                       EstoulsAPI.request(IstinaEditor.getText()).then(x =>
+                       {
+                               $("#blocker-div")[0].remove();
+                               $("#blocker-img")[0].remove();
+                               if (x.success && x.response.trim().length > 0)
+                               {
+                                       var lines = x.response.trim().split("\n");
+                                       var type = lines[0].split("\t").length;
+                                       var results = [];
+                                       if (type == 3)
+                                       {
+                                               for (var i = 0; i < lines.length; i++)
+                                               {
+                                                       results[results.length] = parseInt(lines[i].split("\t")[1]);
+                                               }
+                                               displayResults(results);
+                                       }
+                                       else if (type == 2)
+                                       {
+                                               for (var i = 0; i < lines.length; i++)
+                                               {
+                                                       results[results.length] = parseFloat(lines[i].split("\t")[1].replace("%", "")) / 100.0;
+                                               }
+                                               displayResults(results);
+                                       }
+                                       else
+                                       {
+                                               errorModal(x.response, 1);
+                                       }
+                               }
+                               else if (x.success && x.response.trim().length == 0)
+                               {
+                                       errorModal(x.response, 1);
+                               }
+                               else
+                               {
+                                       errorModal(x.response, 1);
+                               }
+                       }).catch(x =>
+                       {
+                               $("#blocker-div")[0].remove();
+                               $("#blocker-img")[0].remove();
+                               errorModal(x.message, 1);
+                       });
+               });
+               MicroModal.init();
+               function menu(cmd)
+               {
+                       $(".program-instr")[0].style.display = "none";
+                       switch (cmd)
+                       {
+                               case "clear":
+                                       $("#modal-main-title")[0].innerHTML = "Clear Project";
+                                       $("#modal-main-content")[0].innerHTML = "<p>Are you sure you want to clear this project?</p>";
+                                       $("#modal-main-cancel")[0].style.display = "";
+                                       $("#modal-main-confirm")[0].style.display = "";
+                                       $("#modal-main-confirm")[0].setAttribute("onclick", "ProgramTrack.init(); closeModal();");
+                                       MicroModal.show("modal-main");
+                               break;
+                               case "settings":
+                                       var apikey = localStorage.getItem("apikey");
+                                       apikey = (apikey ? apikey : "").replace("'", "").replace("\"", "").replace("\n", "");
+                                       $("#modal-main-title")[0].innerHTML = "API Settings";
+                                       $("#modal-main-content")[0].innerHTML = "<p>Please enter your API key: </p><input type='text' id='apikey' />";
+                                       $("#apikey")[0].style.width = "100%";
+                                       $("#apikey")[0].addEventListener("click", () =>
+                                       {
+                                               $("#apikey")[0].selectionStart = 0;
+                                               $("#apikey")[0].selectionEnd = $("#apikey")[0].value.length;
+                                       });
+                                       $("#apikey")[0].addEventListener("keydown", e =>
+                                       {
+                                               if (e.key == "Enter")
+                                               {
+                                                       $("#modal-main-confirm")[0].click();
+                                               }
+                                       });
+                                       $("#apikey")[0].value = apikey;
+                                       $("#modal-main-confirm")[0].style.display = "";
+                                       $("#modal-main-confirm")[0].setAttribute("onclick", "localStorage.setItem('apikey', $('#modal-main-content input')[0].value); closeModal();");
+                                       MicroModal.show("modal-main");
+                               break;
+                               case "save":
+                                       if (ProgramTrack.STATE[0].length == 0)
+                                       {
+                                               errorModal("You must place at least one logic gate.");
+                                               return;
+                                       }
+                                       var a = document.createElement("a");
+                                       a.href = "data:text/plain," + btoa(escape(JSON.stringify(ProgramTrack.STATE)));
+                                       a.download = "program.txt";
+                                       document.body.appendChild(a);
+                                       a.click();
+                                       a.remove();
+                               break;
+                               case "download":
+                                       downloadResults();
+                               break;
+                               case "open":
+                                       $("#file-input")[0].click();
+                               break;
+                               case "screenshot":
+                                       createBlocker(true);
+                                       var backups =
+                                       [
+                                               resultsChart.data.datasets[0].borderColor,
+                                               resultsChart.data.datasets[0].backgroundColor,
+                                               resultsChart.options.animation.duration,
+                                               resultsChart.config.options.scales.y.ticks.color,
+                                               resultsChart.config.options.scales.x.ticks.color,
+                                               $(".program-track-container")[0].style.height,
+                                               $(".main-table")[0].cellPadding,
+                                               $(".main-table")[0].cellSpacing,
+                                               $(".program-track")[0].cellPadding,
+                                               $(".program-track")[0].cellSpacing
+                                       ];
+                                       $(".program-track-container")[0].style.height = "100%";
+                                       document.body.style.backgroundColor = "rgba(0, 0, 0, 0)";
+                                       $(".main-table tr")[0].style.display = "none";
+                                       $("istina-editor div")[0].style.backgroundColor = "rgba(0, 0, 0, 0)";
+                                       $(".istina-overlay")[0].style.backgroundColor = "rgba(0, 0, 0, 0)";
+                                       $(".istina-overlay")[0].style.color = "black";
+                                       $(".istina-basis")[0].style.opacity = "0%";
+                                       $(".istina-linecounter")[0].style.color = "black"
+                                       resultsChart.data.datasets[0].borderColor = "rgba(0, 0, 0, 255)";
+                                       resultsChart.data.datasets[0].backgroundColor = "rgba(196, 196, 196, 0.5)";
+                                       resultsChart.options.animation.duration = 0;
+                                       resultsChart.config.options.scales.y.ticks.color = "black";
+                                       resultsChart.config.options.scales.x.ticks.color = "black";
+                                       resultsChart.update();
+                                       for (var i = 0; i < $(".program-track td").length; i++)
+                                       {
+                                               $(".program-track td")[i].style.backgroundColor = "rgba(0, 0, 0, 0)";
+                                               $(".program-track td")[i].style.color = "black";
+                                       }
+                                       for (var i = 0; i < $(".program-expand td").length; i++)
+                                       {
+                                               $(".program-expand td")[i].style.backgroundColor = "rgba(0, 0, 0, 0)";
+                                               $(".program-expand td")[i].style.color = "black";
+                                       }
+                                       for (var i = 0; i < $(".measure").length; i++)
+                                       {
+                                               $(".measure")[i].style.filter = "brightness(0%)";
+                                       }
+                                       for (var i = 0; i < $(".istina-overlay span").length; i++)
+                                       {
+                                               $(".istina-overlay span")[i].style.color = "rgba(0, 0, 0, 255)";
+                                       }
+                                       for (var i = 0; i < $("td[onclick*=adjust]").length; i++)
+                                       {
+                                               $("td[onclick*=adjust]")[i].style.display = "none";
+                                       }
+                                       $(".main-table")[0].cellPadding = 0;
+                                       $(".main-table")[0].cellSpacing = 0;
+                                       $(".program-track")[0].cellPadding = 0;
+                                       $(".program-track")[0].cellSpacing = 0;
+                                       domtoimage.toPng($(".main-table")[0]).then(x =>
+                                       {
+
+                                               var a = document.createElement("a");
+                                               a.href = x;
+                                               a.download = "screenshot.png";
+                                               document.body.appendChild(a);
+                                               a.click();
+                                               a.remove();
+                                               $(".program-track-container")[0].style.height = backups[5];
+                                               $(".main-table")[0].border = 1;
+                                               document.body.style.backgroundColor = "";
+                                               $(".main-table tr")[0].style.display = "";
+                                               $("istina-editor div")[0].style.backgroundColor = "";
+                                               $(".istina-overlay")[0].style.backgroundColor = "";
+                                               $(".istina-overlay")[0].style.color = "";
+                                               $(".istina-basis")[0].style.opacity = "";
+                                               $(".istina-linecounter")[0].style.color = ""
+                                               resultsChart.data.datasets[0].borderColor = backups[0];
+                                               resultsChart.data.datasets[0].backgroundColor = backups[1];
+                                               resultsChart.config.options.scales.y.ticks.color = backups[3];
+                                               resultsChart.config.options.scales.x.ticks.color = backups[4];
+                                               resultsChart.update();
+                                               resultsChart.options.animation.duration = backups[2];
+                                               for (var i = 0; i < $(".program-track td").length; i++)
+                                               {
+                                                       $(".program-track td")[i].style.backgroundColor = "";
+                                                       $(".program-track td")[i].style.color = "";
+                                               }
+                                               for (var i = 0; i < $(".program-expand td").length; i++)
+                                               {
+                                                       $(".program-expand td")[i].style.backgroundColor = "";
+                                                       $(".program-expand td")[i].style.color = "";
+                                               }
+                                               for (var i = 0; i < $(".istina-overlay span").length; i++)
+                                                       $(".istina-overlay span")[i].style.color = "";
+                                               for (var i = 0; i < $(".measure").length; i++)
+                                               {
+                                                       $(".measure")[i].style.filter = "";
+                                               }
+                                               $(".program-track-container")[0].style.height[5];
+                                               for (var i = 0; i < $("td[onclick*=adjust]").length; i++)
+                                               {
+                                                       $("td[onclick*=adjust]")[i].style.display = "";
+                                               }
+                                               $(".main-table")[0].cellPadding = backups[6];
+                                               $(".main-table")[0].cellSpacing = backups[7];
+                                               $(".program-track")[0].cellPadding = backups[8];
+                                               $(".program-track")[0].cellSpacing = backups[9];
+                                               $("#blocker-div")[0].remove();
+                                               $("#blocker-img")[0].remove();
+                                       });
+                               break;
+                       }
+               }
+               function errorModal(err, mode)
+               {
+                       if (mode == 1)
+                       {
+                               try
+                               {
+                                       err = err.replace(/[\n]/g, "<br />");
+                               }
+                               catch
+                               {
+                                       err = "Unknown server-side error.";
+                               }
+                       }
+                       $(".program-instr")[0].style.display = "none";
+                       $("#modal-main-confirm")[0].setAttribute("onclick", "closeModal();");
+                       $("#modal-main-cancel")[0].style.display = "none";
+                       $("#modal-main-title")[0].innerHTML = "An Error Occurred";
+                       $("#modal-main-content")[0].innerHTML = "<p style='color: red;'>" + err + "</p>";
+                       MicroModal.show("modal-main");
+               }
+               function closeModal()
+               {
+                       MicroModal.close();
+               }
+               $("#file-input")[0].addEventListener("change", e =>
+               {
+                       var reader = new FileReader();
+                       reader.addEventListener("load", () =>
+                       {
+                               try
+                               {
+                                       ProgramTrack.STATE = JSON.parse(unescape(atob(reader.result)));
+                                       ProgramTrack.draw();
+                               }
+                               catch
+                               {
+                                       errorModal("Could not open this file.");
+                               }
+                       }, false);
+                       if (e.target.files.length == 1)
+                       {
+                               reader.readAsText(e.target.files[0]);
+                       }
+               });
+               function createBlocker(full)
+               {
+                       var r;
+                       if (full == true)
+                       {
+                               r =
+                               {
+                                       top: 0,
+                                       left: 0,
+                                       width: window.innerWidth,
+                                       height: window.innerHeight
+                               };
+                       }
+                       else
+                       {
+                               r = $(".main-table")[0].getBoundingClientRect();
+                       }
+                       var div = document.createElement("div");
+                       div.setAttribute("id", "blocker-div");
+                       div.style.position = "absolute";
+                       div.style.top = Math.round(r.top) + "px";
+                       div.style.left = Math.round(r.left) + "px";
+                       div.style.width = Math.round(r.width) + "px";
+                       div.style.height = Math.round(r.height) + "px";
+                       div.style.backgroundColor = full == true ? getComputedStyle(document.body).backgroundColor : "black";
+                       div.style.opacity = full == true ? "100%" : "50%";
+                       var img = document.createElement("img");
+                       img.setAttribute("id", "blocker-img");
+                       img.src = "imports/spinner.gif";
+                       img.style.position = "absolute";
+                       img.style.width = "64px";
+                       img.style.height = "64px";
+                       img.style.left = Math.round(r.left + r.width / 2 - 32) + "px";
+                       img.style.top = Math.round(r.top + r.height / 2 - 32) + "px";
+                       document.body.appendChild(div);
+                       document.body.appendChild(img);
+               }
+               function modalFixes()
+               {
+                       if (getComputedStyle($("#modal-main")[0]).display == "none")
+                       {
+                               $(".istina-basis")[0].style.visibility = "";
+                       }
+                       else
+                       {
+                               $(".istina-basis")[0].style.visibility = "hidden";
+                       }
+               }
+
+               function getRotationAngle(instr)
+               {
+                       var h = "<input type='number' step='1' value='1' size='4' min='1' max='99' id='constant_1' />";
+                       h += "<select id='pi_1'><option></option><option>pi</option></select>";
+                       h += " / " + h.replace(/[_][1]/g, "_2");
+                       h = "<select id='neg_1'><option>+</option><option>-</option></select>" + h;
+                       $("#modal-main-title")[0].innerHTML = "Rotation Angel";
+                       $("#modal-main-content")[0].innerHTML = "<center>" + h + "</center>";
+                       $("#modal-main-cancel")[0].style.display = "";
+                       $("#modal-main-confirm")[0].style.display = "";
+                       $("#modal-main-confirm")[0].setAttribute("onclick", "setRotationAngle('" + instr + "');");
+                       MicroModal.show("modal-main");
+               }
+
+               function setRotationAngle(instr)
+               {
+                       var numerator = parseInt($("#constant_1")[0].value);
+                       if (isNaN(numerator) || numerator < 0 || numerator > 99) numerator = 1;
+                       var denominator = parseInt($("#constant_2")[0].value);
+                       if (isNaN(numerator) || numerator < 1 || numerator > 99) numerator = 1; 
+                       var pinum = $("#pi_1")[0].value.trim() == "pi" ? "pi" : "";
+                       var piden = $("#pi_2")[0].value.trim() == "pi" ? "pi" : "";
+                       if (pinum == "pi" && piden == "pi") { pinum = ""; piden = ""; }
+                       var negnum = $("#neg_1")[0].value.trim() == "-" ? "-" : "";
+                       var fraction = negnum + numerator + pinum;
+                       if (denominator != 1 || piden != "")
+                       {
+                               fraction += "/" + denominator + piden;
+                       }
+                       if (numerator == denominator)
+                       {
+                               fraction = fraction.replace(numerator + "pi/" + denominator, "1pi");
+                               fraction = fraction.replace(numerator + "/" + denominator + "pi", "1pi");
+                               fraction = fraction.replace(numerator + "/" + denominator, "1");
+                       }
+                       if (fraction.split("/")[0] == "1pi" || fraction.split("/")[0] == "-1pi")
+                       {
+                               var tmp = "";
+                               if (fraction.split("/").length == 2)
+                               {
+                                       tmp = fraction.split("/")[1];
+                               }
+                               fraction = (fraction.includes("-") ? "-" : "") + "pi" + (tmp.length > 0 ? "/" + tmp : "");
+                       }
+
+                       fraction = fraction.replace("pi", "ฯ€").replace("pi", "ฯ€");
+                       closeModal();
+                       ProgramTrack.STATE[ProgramTrack.DRAGHOLD[0]][ProgramTrack.DRAGHOLD[1]] = instr + "<br /><span style='font-size: 12px'>" + fraction + "</span>";
+                       ProgramTrack.draw();
+               }
+               function trackerCollapse(attr)
+               {
+                       while (ProgramTrack.STATE[0].length > 0)
+                       {
+                               if (attr != null)
+                               {
+                                       if (attr.split(",")[1] == ProgramTrack.STATE[0].length - 1)
+                                       {
+                                               break;
+                                       }
+                               }
+                               var lastColumnIsEmpty = true;
+                               for (var i = 0; i < ProgramTrack.STATE.length; i++)
+                               {
+                                       if (ProgramTrack.STATE[i][ProgramTrack.STATE[i].length - 1].trim().length > 0)
+                                       {
+                                               lastColumnIsEmpty = false;
+                                               break;
+                                       }
+                               }
+                               if (lastColumnIsEmpty)
+                               {
+                                       for (var i = 0; i < ProgramTrack.STATE.length; i++)
+                                       {
+                                               ProgramTrack.STATE[i].length -= 1;
+                                       }
+                                       ProgramTrack.draw();
+                               }
+                               else
+                               {
+                                       break;
+                               }
+                       }
+               }
+               //window.onresize = function() { window.location.reload(); }
+       </script>
+       
+</body>
+
diff --git a/src/src.zip b/src/src.zip
new file mode 100644 (file)
index 0000000..c628e38
Binary files /dev/null and b/src/src.zip differ