SVG path conversion tool

by Danny Engelman

HTML

<script src="http://cdn.jsdelivr.net/snap.svg/0.3.0/snap.svg.js"></script>
<h2>
  Convert SVG path
</h2>
<form id=convert>
  org viewBox:<input id=orgVB value=512 size=3 />
  <br>
  best viewBox:<input id=bestVB value=96 size=3 />
  <br>
  Multiplier: <input id=multiplier value=1 size=3 type=number />
  <br>
  Precision: <input id=precision value=0 size=3 type=number />
  <br>
  fill-rule: <input id=fillRule value="evenodd" size=10 type=text />

  <hr>
  <div id=paths>
    <section>
      <label>path: <span></span><textarea id="org">M48 24v12l16-16-16-16v12c-17.68 0-32 14.32-32 32 0 6.28 2.4 12.12 4.96 17.04L26.8 60A23.48 23.48 0 0 1 24 48c0-13.24 10.76-24 24-24zm24 4L69.2 36.8c1.76 3.36 2.8 7.16 2.8 11.2 0 13.24-10.76 24-24 24v-12l-16 16 16 16v-12c17.68 0 32-14.32 32-32 0-6.28-1.84-12.12-4.96-16z</textarea></label>
      <svg>
        <path id="orgPath" fill="red" d="" />
      </svg> 
    </section>
    <section>
      relative: <span></span><textarea id="rel"></textarea>
      <svg>
        <path id="relPath" fill="green" d="" />
      </svg>
    </section>
    <section>
      absolute: <span></span><textarea id="abs"></textarea>
      <svg>
        <path id="absPath" fill="blue" d="" />
      </svg>
    </section>
    <section>
      multiplied*: <span></span><textarea id="mul"></textarea>
      <svg>
        <path id="mulPath" fill="hotpink" d="" />
      </svg>
    </section>
  </div>
</form>
<footer>
</footer>

CSS

body {
	font: 100%/1.5 Helvetica Neue, sans-serif;
	margin: 1em;
}
input{
  margin-left:1em;
   font-size:100%;
}
#paths {
	display:grid;
	grid-template-columns: repeat(auto-fit,minmax(50px,1fr));
	grid-gap:1em;
	width: 100%;
}

textarea {
	width: 100%;
	height: 50vh;
}
svg{
  width:100%;
  background:lightgrey;
}

JavaScript

window.onload = () => {
  const sanitize = (s, p = precision.value, m = 1) => {
    return String(s).replace(/[0-9]*\.[0-9]*/g, x => {
      let val = (+x) * Number(m);
      val = +val.toFixed(Number(p));
      if(p==1) return Math.round(val*2)/2;
      else return val;
    }).replace(/,/g, " ").replace(/ -/g,"-");
  }

  const setViewBox = (svg, v) => svg.parentNode.setAttribute('viewBox', `0 0 ${v} ${v}`);
  const setSVG = (ta, svg, path) => {
    svg.setAttribute('d', path);
    svg.setAttribute('fill-rule', `${String(fillRule.value)}`);
    setViewBox(svg, orgVB.value * (svg == mulPath ? multiplier.value : 1));
    ta.innerText = path;
    ta.previousSibling.innerText = path.length + " bytes";
  }
  const recalc = (path = org.value) => {
    localStorage.setItem("path", path);
    setSVG(org, orgPath, path);
    setSVG(mul, mulPath, sanitize(path, precision.value,  multiplier.value));
    setSVG(rel, relPath, sanitize(Snap.path.toRelative(path)));
    setSVG(abs, absPath, sanitize(Snap.path.toAbsolute(path)));
  }
  convert.oninput = (evt) => {
    store(evt.target.id, evt.target.value);
    recalc();
  }
  const store = (name, value = false) => localStorage[(value ? "set" : value == undefined ? "remove" : "get") + "Item"]("svgedit_"+name, value);

  const restore = (name, val = "") => document.getElementById(name).value = store(name) || val;

  restore("orgVB", 24);
  restore("bestVB", 96);
  restore("bestVB", 1);
  restore("bestVB", 0);
  restore("multiplier", 1);
  restore("precision", 0);
  restore("fillRule")

  recalc(localStorage.getItem("path") || undefined);
  // org.onfocus = rel.onfocus = abs.onfocus = (evt)=>evt.target.select();
  // if (org.value) org.oninput();
}