jcunews

MechanicDB.hta

Feb 4th, 2022 (edited)
158
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <!doctype html>
  2. <html>
  3. <head>
  4.  
  5.   <!--
  6.  MechanicDB v1.0.1. AGPL v3. February 2022.
  7.  https://www.reddit.com/user/jcunews1
  8.  https://greasyfork.org/en/users/85671-jcunews
  9.  https://pastebin.com/u/jcunews
  10.  
  11.  Context:
  12.  https://www.reddit.com/r/Batch/comments/sji28h/batch_script_for_mechanic/
  13.  
  14.  Database file is in CSV format and in stored at:
  15.  
  16.    %USERPROFILE%\MechanicDB.csv
  17.  
  18.  e.g.: C:\Users\myusername\MechanicDB.csv
  19.  
  20.  Save this code as `MechanicDB.hta`. NOT as `MechanicDB.html` or `MechanicDB.htm`.
  21.  -->
  22.  
  23.   <meta charset=utf-8 />
  24.   <meta http-equiv="x-ua-compatible" content="IE=9" />
  25.   <title>MechanicDB</title>
  26.   <style>
  27.     body { margin: 3.5em 0 0 0; overflow-y: scroll; text-align: center }
  28.     body, input, button { min-width: 3em; font: 16pt/normal sans-serif }
  29.     input, button { padding: 0 .2em; width: auto; font: inherit }
  30.     #form {
  31.       display: block; position: fixed; left: 0; top: 0; right: 0;
  32.       border-bottom: .1em solid #000; padding: .5em 0; background: #dde;
  33.     }
  34.     #lTime { position: absolute; left: .5em; top: .3em; font-size: 10pt }
  35.     #tList td { padding: .5em .2em; max-width: 75vw }
  36.     #tList a {
  37.       display: inline-block; margin: .3em; border: 1px solid #aaa;
  38.       padding: .1em .2em 0 .2em; background: #eee; text-decoration: none;
  39.     }
  40.     #tList a:hover { background: #00c; color: #cc0 }
  41.     #bList { position: absolute; top: .5em; right: .5em }
  42.     #eQuery { margin-right: .5em; padding: 0 .3em; width: 7.7em }
  43.     #tList, #tNewRecord, #tMileages {
  44.       display: none; margin: 1em auto 0 auto; border: .1em solid #ddd
  45.     }
  46.     #pRecord { display: none; margin: 0 auto }
  47.     #tMileages {
  48.       display: table; margin-top: .5em; border: none; border-spacing: .2em;
  49.     }
  50.     th, td { padding: .1em .2em; font-weight: normal }
  51.     #tRecord tr:nth-child(4) td { padding: 1em .2em .5em .2em }
  52.     #eAddMileage, #eNewMileage { width: 4em }
  53.     #bAddMileage { margin-left: .5em }
  54.     #tMileages tr:nth-child(2) th { background: #666 }
  55.     #tMileages td { background: #ddd }
  56.     #tMileages td:first-child { padding-right: 1em; text-align: right }
  57.     #tMileages tr:nth-child(2n+1) td { background: #fff }
  58.     #tMileages button {
  59.       display: none; position:absolute; margin-left: 1em; min-width: 1.5em;
  60.       font-size: 13pt
  61.     }
  62.     #tMileages tr:last-child button { display: inline-block }
  63.     #tNewRecord { border-spacing: .3em; width: 25.5em }
  64.     th { background: #000; color: #fff }
  65.     td { text-align: left }
  66.     #tNewRecord td:first-child { width: 5em; white-space: nowrap }
  67.     #tNewRecord tr:last-child td { padding-top: 1em; text-align:center }
  68.   </style>
  69.   <style id=ls></style>
  70.  
  71. </head>
  72. <body>
  73.  
  74.   <hta:application qwindowstate=maximize></hta:application>
  75.  
  76.   <div id=form>
  77.     <form id=fQuery onsubmit="return query()">
  78.       <div id=lTime></div>
  79.       License Plate:
  80.       <input id=eQuery tabindex=1 onkeydown="setTimeout(qinput,0)" />
  81.       <button id=bQuery type=submit tabindex=2>Query</button>
  82.     </form>
  83.     <button id=bList tabindex=3 onclick="return list()">List</button>
  84.   </div>
  85.  
  86.   <p id=lMessage>Please enter a license plate.</p>
  87.  
  88.   <table id=tList>
  89.     <tr><th>License Plates</th></tr>
  90.     <tr><td></td></tr>
  91.   </table>
  92.  
  93.   <div id=pRecord>
  94.     <form id=fMileage onsubmit="return addMileage()">
  95.       <table id=tRecord>
  96.         <tr><td>License Plate:</td><td id=lLicensePlate></td></tr>
  97.         <tr><td>Created:</td><td id=lCreated></td></tr>
  98.         <tr><td>Last Updated:</td><td id=lUpdated></td></tr>
  99.         <tr>
  100.           <td>Add Mileage:</td>
  101.           <td>
  102.             <input id=eAddMileage /> miles
  103.             <button id=bAddMileage type=submit>Add</button>
  104.           </td>
  105.         </tr>
  106.       </table>
  107.     </form>
  108.     <table id=tMileages>
  109.       <tr><th colspan=3>Mileages</th></tr>
  110.       <tr><th>Number</th><th>Time</th><th>Mileage</th></tr>
  111.     </table>
  112.   </div>
  113.  
  114.   <form id=fNewRecord onsubmit="return newRecord()">
  115.     <table id=tNewRecord>
  116.       <tr><td>License Plate:</td><td id=lNewLicensePlate></td></tr>
  117.       <tr><td>Mileage:</td><td><input id=eNewMileage /> miles</td></tr>
  118.       <tr>
  119.         <td colspan=2>
  120.           <button id=bNewRecord type=submit>OK</button>
  121.         </td>
  122.       </tr>
  123.     </table>
  124.   </form>
  125.  
  126.   <script>
  127.  
  128. function qinput() {
  129.   if (!tList.style.display) return;
  130.   if (s = eQuery.value.trim()) {
  131.     ls.innerHTML = '\
  132. #tList a { display: none }\
  133. #tList a[plate*="' + s.toUpperCase() + '"] { display:inline-block }'
  134.   } else ls.innerHTML = ""
  135. }
  136.  
  137. function dispRec() {
  138.   query(this.getAttribute("plate"));
  139.   return false
  140. }
  141.  
  142. function list() {
  143.   eQuery.value = "";
  144.   (c = tList.rows[1].cells[0]).innerHTML = "";
  145.   ks = Object.keys(data).sort();
  146.   for (i = 0; i < arr.length; i++) {
  147.    (e = document.createElement("A")).innerText = ks[i];
  148.    e.href = "#";
  149.    e.setAttribute("plate", ks[i]);
  150.    e.onclick = dispRec;
  151.    c.appendChild(e);
  152.  }
  153.  lMessage.style.display = "none";
  154.  tList.style.display = "table";
  155.  pRecord.style.display = "";
  156.  tNewRecord.style.display = "";
  157.  eQuery.focus();
  158.  return false
  159. }
  160.  
  161. function query(s) {
  162.  if (s = s || eQuery.value.trim().toUpperCase()) {
  163.  
  164.    tList.style.display = "";
  165.    if (rec = data[s]) {
  166.  
  167.      lMessage.style.display = "none";
  168.      lLicensePlate.innerText = s;
  169.      lCreated.innerText = (new Date(rec.created)).toLocaleString();
  170.      lUpdated.innerText = (new Date(rec.lastUpdated)).toLocaleString();
  171.      b = tMileages.tBodies[0].cloneNode(true);
  172.      while (b.rows.length > 2) b.deleteRow(2);
  173.       for (i = 0; i < rec.mileages.length; i++) {
  174.        r = b.insertRow();
  175.        r.insertCell().innerText = i + 1;
  176.        r.insertCell().innerText =
  177.          (new Date(parseInt(rec.mileages[i].time))).toLocaleString();
  178.        (c = r.insertCell()).innerHTML =
  179.          rec.mileages[i].mileage + " miles<button>X</button>";
  180.         c.firstElementChild.rec = rec;
  181.         c.firstElementChild.mIndex = i;
  182.         c.firstElementChild.onclick = delMileage;
  183.       }
  184.       tMileages.replaceChild(b, tMileages.tBodies[0]);
  185.       eAddMileage.value = "";
  186.       lMessage.innerText = "License plate found.";
  187.       pRecord.style.display = "table";
  188.       tNewRecord.style.display = "";
  189.       eAddMileage.focus()
  190.  
  191.     } else {
  192.  
  193.       lNewLicensePlate.innerText = s;
  194.       eNewMileage.value = "";
  195.       lMessage.style.display = "";
  196.       lMessage.innerText = "\
  197. No matching license plate found.\n\
  198. Enter other license plate above, or enter data for a new license plate below.";
  199.       pRecord.style.display = "";
  200.       tNewRecord.style.display = "table";
  201.       eNewMileage.focus()
  202.     }
  203.  
  204.   } else {
  205.     alert("License plate must not be empty.");
  206.     eQuery.focus()
  207.   }
  208.   return false
  209. }
  210.  
  211. function saveData() {
  212.   fs.copyFile(dataFile, dataFile.replace(".csv", ".bak"), true);
  213.   fData = fs.createTextFile(dataFile, true);
  214.   ks = Object.keys(data);
  215.   buf = [];
  216.   for (i = ks.length - 1; i >= 0; i--) {
  217.     rec = data[ks[i]];
  218.     ms = [];
  219.     for (j = 0; j < rec.mileages.length; j++) {
  220.      ms.push(rec.mileages[j].time);
  221.      ms.push(rec.mileages[j].mileage);
  222.    }
  223.    arr = [rec.licensePlate, rec.created, rec.lastUpdated];
  224.    Array.prototype.push.apply(arr, ms);
  225.    buf.push(arr.join(","))
  226.  }
  227.  fData.writeLine(buf.join("\n"));
  228.  fData.close();
  229. }
  230.  
  231. function addMileage() {
  232.  if (s = eAddMileage.value.trim()) {
  233.    if (/^\d+$/.test(s) && ((s = parseInt(s)) >= 0)) {
  234.       t = Date.now();
  235.       rec.mileages.push({time: t, mileage: s});
  236.       rec.lastUpdated = t;
  237.       saveData();
  238.       lUpdated.innerText = (new Date(t)).toLocaleString();
  239.       (r = tMileages.insertRow()).insertCell().innerText = rec.mileages.length;
  240.       r.insertCell().innerText = (new Date(t)).toLocaleString();
  241.       (c = r.insertCell()).innerHTML = s + " miles<button>X</button>";
  242.       c.firstElementChild.mIndex = rec.mileages.length - 1;
  243.       c.firstElementChild.onclick = delMileage
  244.     } else alert("Mileage must be a positive integer number.")
  245.   } else alert("Mileage must not be empty.");
  246.   eAddMileage.select();
  247.   eAddMileage.focus()
  248.   return false
  249. }
  250.  
  251. function delMileage() {
  252.   if (!confirm("Delete this mileage entry?")) return;
  253.   rec.mileages.pop();
  254.   rec.lastUpdated = (t = Date.now());
  255.   saveData();
  256.   lUpdated.innerText = (new Date(t)).toLocaleString();
  257.   (r = this.parentNode.parentNode).parentNode.removeChild(r);
  258.   eAddMileage.focus()
  259. }
  260.  
  261. function newRecord() {
  262.   if (s = eNewMileage.value.trim()) {
  263.     if (/^\d+$/.test(s) && ((s = parseInt(s)) >= 0)) {
  264.      t = Date.now();
  265.       data[lNewLicensePlate.innerText] = {
  266.         licensePlate: lNewLicensePlate.innerText,
  267.         created: t,
  268.         lastUpdated: t,
  269.         mileages: [{time: t, mileage: s}]
  270.       };
  271.       saveData();
  272.       query(lNewLicensePlate.innerText)
  273.     } else {
  274.       alert("Mileage must be a positive integer number.");
  275.       eNewMileage.select();
  276.       eNewMileage.focus()
  277.     }
  278.   } else {
  279.     alert("Mileage must not be empty.");
  280.     eNewMileage.focus()
  281.   }
  282.   return false
  283. }
  284.  
  285. String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g) };
  286. eQuery = fQuery.eQuery;
  287. eAddMileage = fMileage.eAddMileage;
  288. eNewMileage = fNewRecord.eNewMileage;
  289. setTimeout(function() { eQuery.focus() }, 0);
  290. fs = new ActiveXObject("scripting.filesystemobject");
  291. ws = new ActiveXObject("wscript.shell");
  292. dataFile = ws.environment("process")("userprofile") + "\\MechanicDB.csv";
  293. fData = fs.openTextFile(dataFile, 1, true);
  294. if (!fData.atEndOfStream) {
  295.   buf = fData.readAll().split("\n")
  296. } else buf = [];
  297. fData.close();
  298. data = {};
  299. for (i = buf.length - 1; i >= 0; i--) {
  300.   if (!(buf[i] = buf[i].trim())) continue;
  301.   arr = buf[i].split(",");
  302.   ms = [];
  303.   for (j = 0; j < (arr.length - 3) / 2; j++) {
  304.    ms.push({
  305.      time: parseInt(arr[j * 2 + 3]), mileage: parseInt(arr[j * 2 + 4])
  306.    })
  307.  }
  308.  rec = {
  309.    licensePlate: arr[0],
  310.    created: parseInt(arr[1]),
  311.    lastUpdated: parseInt(arr[2]),
  312.    mileages: ms
  313.  };
  314.  data[rec.licensePlate] = rec
  315. }
  316. (function updTime(t) {
  317.  lTime.innerText =
  318.    (t = new Date).toLocaleDateString().replace(", ", "\n") + "\n" + t.toLocaleTimeString();
  319.  setTimeout(updTime, 50)
  320. })()
  321.  
  322.  </script>
  323.  
  324. </body>
  325. </html>
  326.  
RAW Paste Data Copied