input-knobs and json modules
commit
cebde38caa
@ -0,0 +1,18 @@
|
|||||||
|
venv/
|
||||||
|
|
||||||
|
*.pyc
|
||||||
|
__pycache__/
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.egg-info/
|
||||||
|
|
||||||
|
.env
|
||||||
|
.vscode
|
@ -0,0 +1,41 @@
|
|||||||
|
import os
|
||||||
|
from flask import Flask, send_from_directory
|
||||||
|
from . import prefix
|
||||||
|
|
||||||
|
|
||||||
|
def create_app(test_config=None):
|
||||||
|
# Create and configure the Flask App
|
||||||
|
app = Flask(__name__, instance_relative_config=True)
|
||||||
|
app.config.from_mapping(
|
||||||
|
SECRET_KEY="dev",
|
||||||
|
)
|
||||||
|
|
||||||
|
if test_config is None:
|
||||||
|
# load the instance config, if it exists, when not testing
|
||||||
|
app.config.from_pyfile("config.py", silent=True)
|
||||||
|
else:
|
||||||
|
# load the test config if passed in
|
||||||
|
app.config.from_mapping(test_config)
|
||||||
|
|
||||||
|
# ensure the instance folder exists
|
||||||
|
try:
|
||||||
|
os.makedirs(os.environ.get('MODULES', 'modules'))
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@app.route("/favicon.ico")
|
||||||
|
def favicon():
|
||||||
|
return send_from_directory(
|
||||||
|
os.path.join(app.root_path, "static"),
|
||||||
|
"favicon.ico",
|
||||||
|
mimetype="image/vnd.microsoft.icon",
|
||||||
|
)
|
||||||
|
|
||||||
|
from . import module
|
||||||
|
app.register_blueprint(module.bp)
|
||||||
|
|
||||||
|
app.wsgi_app = prefix.PrefixMiddleware(
|
||||||
|
app.wsgi_app, prefix=os.environ.get("URL_PREFIX", "")
|
||||||
|
)
|
||||||
|
|
||||||
|
return app
|
@ -0,0 +1,23 @@
|
|||||||
|
from flask import Blueprint, render_template, request
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
bp = Blueprint('module', __name__, url_prefix="/")
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/', methods=['GET', 'POST'])
|
||||||
|
def module():
|
||||||
|
if request.method == 'POST':
|
||||||
|
preset = {}
|
||||||
|
for key in request.form.keys():
|
||||||
|
preset[key] = request.form.get(key)
|
||||||
|
name = preset.get('name', '')
|
||||||
|
filename = f"{name}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.json"
|
||||||
|
with open(os.path.join(os.environ.get('MODULES'), filename), 'w') as f:
|
||||||
|
f.write(json.dumps(preset))
|
||||||
|
|
||||||
|
modules = [os.path.splitext(filename)[0]
|
||||||
|
for filename in os.listdir(os.environ.get('MODULES'))]
|
||||||
|
|
||||||
|
return render_template('module.html', modules=modules)
|
@ -0,0 +1,14 @@
|
|||||||
|
class PrefixMiddleware(object):
|
||||||
|
def __init__(self, app, prefix=""):
|
||||||
|
self.app = app
|
||||||
|
self.prefix = prefix
|
||||||
|
|
||||||
|
def __call__(self, environ, start_response):
|
||||||
|
|
||||||
|
if environ["PATH_INFO"].startswith(self.prefix):
|
||||||
|
environ["PATH_INFO"] = environ["PATH_INFO"][len(self.prefix) :]
|
||||||
|
environ["SCRIPT_NAME"] = self.prefix
|
||||||
|
return self.app(environ, start_response)
|
||||||
|
else:
|
||||||
|
start_response("404", [("Content-Type", "text/plain")])
|
||||||
|
return ["This url does not belong to the app.".encode()]
|
@ -0,0 +1,63 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module {
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: start;
|
||||||
|
width: 480px;
|
||||||
|
height: 240px;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
|
||||||
|
padding: 32px;
|
||||||
|
border: 1px solid currentColor;
|
||||||
|
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control label {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control + .control {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module input[type='submit'] {
|
||||||
|
position: absolute;
|
||||||
|
right: 32px;
|
||||||
|
bottom: 32px;
|
||||||
|
border: 1px solid currentColor;
|
||||||
|
background: none;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module #name {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 32px;
|
||||||
|
left: 32px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
border: 1px solid currentColor;
|
||||||
|
}
|
@ -0,0 +1,375 @@
|
|||||||
|
window.addEventListener("load", () => {
|
||||||
|
let op = window.inputKnobsOptions || {};
|
||||||
|
op.knobWidth = op.knobWidth || op.knobDiameter || 64;
|
||||||
|
op.knobHeight = op.knobHeight || op.knobDiameter || 64;
|
||||||
|
op.sliderWidth = op.sliderWidth || op.sliderDiameter || 128;
|
||||||
|
op.sliderHeight = op.sliderHeight || op.sliderDiameter || 20;
|
||||||
|
op.switchWidth = op.switchWidth || op.switchDiameter || 24;
|
||||||
|
op.switchHeight = op.switchHeight || op.switchDiameter || 24;
|
||||||
|
op.fgcolor = op.fgcolor || "#f00";
|
||||||
|
op.bgcolor = op.bgcolor || "#000";
|
||||||
|
op.knobMode = op.knobMode || "linear";
|
||||||
|
op.sliderMode = op.sliderMode || "relative";
|
||||||
|
let styles = document.createElement("style");
|
||||||
|
styles.innerHTML = `input[type=range].input-knob,input[type=range].input-slider{
|
||||||
|
-webkit-appearance:none;
|
||||||
|
-moz-appearance:none;
|
||||||
|
border:none;
|
||||||
|
box-sizing:border-box;
|
||||||
|
overflow:hidden;
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
background-size:100% 100%;
|
||||||
|
background-position:0px 0%;
|
||||||
|
background-color:transparent;
|
||||||
|
touch-action:none;
|
||||||
|
}
|
||||||
|
input[type=range].input-knob{
|
||||||
|
width:${op.knobWidth}px; height:${op.knobHeight}px;
|
||||||
|
}
|
||||||
|
input[type=range].input-slider{
|
||||||
|
width:${op.sliderWidth}px; height:${op.sliderHeight}px;
|
||||||
|
}
|
||||||
|
input[type=range].input-knob::-webkit-slider-thumb,input[type=range].input-slider::-webkit-slider-thumb{
|
||||||
|
-webkit-appearance:none;
|
||||||
|
opacity:0;
|
||||||
|
}
|
||||||
|
input[type=range].input-knob::-moz-range-thumb,input[type=range].input-slider::-moz-range-thumb{
|
||||||
|
-moz-appearance:none;
|
||||||
|
height:0;
|
||||||
|
border:none;
|
||||||
|
}
|
||||||
|
input[type=range].input-knob::-moz-range-track,input[type=range].input-slider::-moz-range-track{
|
||||||
|
-moz-appearance:none;
|
||||||
|
height:0;
|
||||||
|
border:none;
|
||||||
|
}
|
||||||
|
input[type=checkbox].input-switch,input[type=radio].input-switch {
|
||||||
|
width:${op.switchWidth}px;
|
||||||
|
height:${op.switchHeight}px;
|
||||||
|
-webkit-appearance:none;
|
||||||
|
-moz-appearance:none;
|
||||||
|
background-size:100% 200%;
|
||||||
|
background-position:0% 0%;
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
border:none;
|
||||||
|
border-radius:0;
|
||||||
|
background-color:transparent;
|
||||||
|
}
|
||||||
|
input[type=checkbox].input-switch:checked,input[type=radio].input-switch:checked {
|
||||||
|
background-position:0% 100%;
|
||||||
|
}`;
|
||||||
|
document.head.appendChild(styles);
|
||||||
|
let makeKnobFrames = (fr, fg, bg) => {
|
||||||
|
let r = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="${
|
||||||
|
fr * 64
|
||||||
|
}" viewBox="0 0 64 ${fr * 64}" preserveAspectRatio="none">
|
||||||
|
<defs><g id="K"><circle cx="32" cy="32" r="30" fill="${bg}"/>
|
||||||
|
<line x1="32" y1="28" x2="32" y2="7" stroke-linecap="round" stroke-width="6" stroke="${fg}"/></g></defs>
|
||||||
|
<use xlink:href="#K" transform="rotate(-135,32,32)"/>`;
|
||||||
|
for (let i = 1; i < fr; ++i)
|
||||||
|
r += `<use xlink:href="#K" transform="translate(0,${64 * i}) rotate(${
|
||||||
|
-135 + (270 * i) / fr
|
||||||
|
},32,32)"/>`;
|
||||||
|
return r + "</svg>";
|
||||||
|
};
|
||||||
|
let makeHSliderFrames = (fr, fg, bg, w, h) => {
|
||||||
|
let r = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${w}" height="${
|
||||||
|
fr * h
|
||||||
|
}" viewBox="0 0 ${w} ${fr * h}" preserveAspectRatio="none">
|
||||||
|
<defs><g id="B"><rect x="0" y="0" width="${w}" height="${h}" rx="${h / 2}" ry="${
|
||||||
|
h / 2
|
||||||
|
}" fill="${bg}"/></g>
|
||||||
|
<g id="K"><circle x="${w / 2}" y="0" r="${(h / 2) * 0.9}" fill="${fg}"/></g></defs>`;
|
||||||
|
for (let i = 0; i < fr; ++i) {
|
||||||
|
r += `<use xlink:href="#B" transform="translate(0,${h * i})"/>`;
|
||||||
|
r += `<use xlink:href="#K" transform="translate(${h / 2 + ((w - h) * i) / 100},${
|
||||||
|
h / 2 + h * i
|
||||||
|
})"/>`;
|
||||||
|
}
|
||||||
|
return r + "</svg>";
|
||||||
|
};
|
||||||
|
let makeVSliderFrames = (fr, fg, bg, w, h) => {
|
||||||
|
let r = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${w}" height="${
|
||||||
|
fr * h
|
||||||
|
}" viewBox="0 0 ${w} ${fr * h}" preserveAspectRatio="none">
|
||||||
|
<defs><rect id="B" x="0" y="0" width="${w}" height="${h}" rx="${w / 2}" ry="${w / 2}" fill="${bg}"/>
|
||||||
|
<circle id="K" x="0" y="0" r="${(w / 2) * 0.9}" fill="${fg}"/></defs>`;
|
||||||
|
for (let i = 0; i < fr; ++i) {
|
||||||
|
r += `<use xlink:href="#B" transform="translate(0,${h * i})"/>`;
|
||||||
|
r += `<use xlink:href="#K" transform="translate(${w / 2} ${
|
||||||
|
h * (i + 1) - w / 2 - (i * (h - w)) / 100
|
||||||
|
})"/>`;
|
||||||
|
}
|
||||||
|
return r + "</svg>";
|
||||||
|
};
|
||||||
|
let initSwitches = (el) => {
|
||||||
|
let w, h, d, fg, bg;
|
||||||
|
if (el.inputKnobs) return;
|
||||||
|
el.inputKnobs = {};
|
||||||
|
el.refresh = () => {
|
||||||
|
let src = el.getAttribute("data-src");
|
||||||
|
d = +el.getAttribute("data-diameter");
|
||||||
|
let st = document.defaultView.getComputedStyle(el, null);
|
||||||
|
w = parseFloat(el.getAttribute("data-width") || d || st.width);
|
||||||
|
h = parseFloat(el.getAttribute("data-height") || d || st.height);
|
||||||
|
bg = el.getAttribute("data-bgcolor") || op.bgcolor;
|
||||||
|
fg = el.getAttribute("data-fgcolor") || op.fgcolor;
|
||||||
|
el.style.width = w + "px";
|
||||||
|
el.style.height = h + "px";
|
||||||
|
if (src) el.style.backgroundImage = "url(" + src + ")";
|
||||||
|
else {
|
||||||
|
let minwh = Math.min(w, h);
|
||||||
|
let svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${
|
||||||
|
h * 2
|
||||||
|
}" viewBox="0 0 ${w} ${h * 2}" preserveAspectRatio="none">
|
||||||
|
<g><rect fill="${bg}" x="1" y="1" width="${w - 2}" height="${h - 2}" rx="${minwh * 0.25}" ry="${
|
||||||
|
minwh * 0.25
|
||||||
|
}"/>
|
||||||
|
<rect fill="${bg}" x="1" y="${h + 1}" width="${w - 2}" height="${h - 2}" rx="${minwh * 0.25}" ry="${
|
||||||
|
minwh * 0.25
|
||||||
|
}"/>
|
||||||
|
<circle fill="${fg}" cx="${w * 0.5}" cy="${h * 1.5}" r="${minwh * 0.25}"/></g></svg>`;
|
||||||
|
el.style.backgroundImage = "url(data:image/svg+xml;base64," + btoa(svg) + ")";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
el.refresh();
|
||||||
|
};
|
||||||
|
let initKnobs = (el) => {
|
||||||
|
let w, h, d, fg, bg;
|
||||||
|
if (el.inputKnobs) {
|
||||||
|
el.redraw();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let ik = (el.inputKnobs = {});
|
||||||
|
el.refresh = () => {
|
||||||
|
d = +el.getAttribute("data-diameter");
|
||||||
|
let st = document.defaultView.getComputedStyle(el, null);
|
||||||
|
w = parseFloat(el.getAttribute("data-width") || d || st.width);
|
||||||
|
h = parseFloat(el.getAttribute("data-height") || d || st.height);
|
||||||
|
bg = el.getAttribute("data-bgcolor") || op.bgcolor;
|
||||||
|
fg = el.getAttribute("data-fgcolor") || op.fgcolor;
|
||||||
|
ik.sensex = ik.sensey = 200;
|
||||||
|
if (el.className.indexOf("input-knob") >= 0) ik.itype = "k";
|
||||||
|
else {
|
||||||
|
if (w >= h) {
|
||||||
|
ik.itype = "h";
|
||||||
|
ik.sensex = w - h;
|
||||||
|
ik.sensey = Infinity;
|
||||||
|
el.style.backgroundSize = "auto 100%";
|
||||||
|
} else {
|
||||||
|
ik.itype = "v";
|
||||||
|
ik.sensex = Infinity;
|
||||||
|
ik.sensey = h - w;
|
||||||
|
el.style.backgroundSize = "100% auto";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
el.style.width = w + "px";
|
||||||
|
el.style.height = h + "px";
|
||||||
|
ik.frameheight = h;
|
||||||
|
let src = el.getAttribute("data-src");
|
||||||
|
if (src) {
|
||||||
|
el.style.backgroundImage = `url(${src})`;
|
||||||
|
let sp = +el.getAttribute("data-sprites");
|
||||||
|
if (sp) ik.sprites = sp;
|
||||||
|
else ik.sprites = 0;
|
||||||
|
if (ik.sprites >= 1) el.style.backgroundSize = `100% ${(ik.sprites + 1) * 100}%`;
|
||||||
|
else if (ik.itype != "k") {
|
||||||
|
el.style.backgroundColor = bg;
|
||||||
|
el.style.borderRadius = Math.min(w, h) * 0.25 + "px";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let svg;
|
||||||
|
switch (ik.itype) {
|
||||||
|
case "k":
|
||||||
|
svg = makeKnobFrames(101, fg, bg);
|
||||||
|
break;
|
||||||
|
case "h":
|
||||||
|
svg = makeHSliderFrames(101, fg, bg, w, h);
|
||||||
|
break;
|
||||||
|
case "v":
|
||||||
|
svg = makeVSliderFrames(101, fg, bg, w, h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ik.sprites = 100;
|
||||||
|
el.style.backgroundImage = "url(data:image/svg+xml;base64," + btoa(svg) + ")";
|
||||||
|
el.style.backgroundSize = `100% ${(ik.sprites + 1) * 100}%`;
|
||||||
|
}
|
||||||
|
ik.valrange = {
|
||||||
|
min: +el.min,
|
||||||
|
max: el.max == "" ? 100 : +el.max,
|
||||||
|
step: el.step == "" ? 1 : +el.step,
|
||||||
|
};
|
||||||
|
el.redraw(true);
|
||||||
|
};
|
||||||
|
el.setValue = (v) => {
|
||||||
|
v =
|
||||||
|
Math.round((v - ik.valrange.min) / ik.valrange.step) * ik.valrange.step +
|
||||||
|
ik.valrange.min;
|
||||||
|
if (v < ik.valrange.min) v = ik.valrange.min;
|
||||||
|
if (v > ik.valrange.max) v = ik.valrange.max;
|
||||||
|
el.value = v;
|
||||||
|
if (el.value != ik.oldvalue) {
|
||||||
|
el.setAttribute("value", el.value);
|
||||||
|
el.redraw();
|
||||||
|
let event = document.createEvent("HTMLEvents");
|
||||||
|
event.initEvent("input", false, true);
|
||||||
|
el.dispatchEvent(event);
|
||||||
|
ik.oldvalue = el.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ik.pointerdown = (ev) => {
|
||||||
|
el.focus();
|
||||||
|
const evorg = ev;
|
||||||
|
if (ev.touches) ev = ev.touches[0];
|
||||||
|
let rc = el.getBoundingClientRect();
|
||||||
|
let cx = (rc.left + rc.right) * 0.5,
|
||||||
|
cy = (rc.top + rc.bottom) * 0.5;
|
||||||
|
let dx = ev.clientX,
|
||||||
|
dy = ev.clientY;
|
||||||
|
let da = Math.atan2(ev.clientX - cx, cy - ev.clientY);
|
||||||
|
if (ik.itype == "k" && op.knobMode == "circularabs") {
|
||||||
|
dv =
|
||||||
|
ik.valrange.min +
|
||||||
|
((da / Math.PI) * 0.75 + 0.5) * (ik.valrange.max - ik.valrange.min);
|
||||||
|
el.setValue(dv);
|
||||||
|
}
|
||||||
|
if (ik.itype != "k" && op.sliderMode == "abs") {
|
||||||
|
dv =
|
||||||
|
(ik.valrange.min + ik.valrange.max) * 0.5 +
|
||||||
|
((dx - cx) / ik.sensex - (dy - cy) / ik.sensey) *
|
||||||
|
(ik.valrange.max - ik.valrange.min);
|
||||||
|
el.setValue(dv);
|
||||||
|
}
|
||||||
|
ik.dragfrom = {
|
||||||
|
x: ev.clientX,
|
||||||
|
y: ev.clientY,
|
||||||
|
a: Math.atan2(ev.clientX - cx, cy - ev.clientY),
|
||||||
|
v: +el.value,
|
||||||
|
};
|
||||||
|
document.addEventListener("mousemove", ik.pointermove);
|
||||||
|
document.addEventListener("mouseup", ik.pointerup);
|
||||||
|
document.addEventListener("touchmove", ik.pointermove);
|
||||||
|
document.addEventListener("touchend", ik.pointerup);
|
||||||
|
document.addEventListener("touchcancel", ik.pointerup);
|
||||||
|
document.addEventListener("touchstart", ik.preventScroll);
|
||||||
|
evorg.preventDefault();
|
||||||
|
evorg.stopPropagation();
|
||||||
|
};
|
||||||
|
ik.pointermove = (ev) => {
|
||||||
|
let dv;
|
||||||
|
let rc = el.getBoundingClientRect();
|
||||||
|
let cx = (rc.left + rc.right) * 0.5,
|
||||||
|
cy = (rc.top + rc.bottom) * 0.5;
|
||||||
|
if (ev.touches) ev = ev.touches[0];
|
||||||
|
let dx = ev.clientX - ik.dragfrom.x,
|
||||||
|
dy = ev.clientY - ik.dragfrom.y;
|
||||||
|
let da = Math.atan2(ev.clientX - cx, cy - ev.clientY);
|
||||||
|
switch (ik.itype) {
|
||||||
|
case "k":
|
||||||
|
switch (op.knobMode) {
|
||||||
|
case "linear":
|
||||||
|
dv =
|
||||||
|
(dx / ik.sensex - dy / ik.sensey) *
|
||||||
|
(ik.valrange.max - ik.valrange.min);
|
||||||
|
if (ev.shiftKey) dv *= 0.2;
|
||||||
|
el.setValue(ik.dragfrom.v + dv);
|
||||||
|
break;
|
||||||
|
case "circularabs":
|
||||||
|
if (!ev.shiftKey) {
|
||||||
|
dv =
|
||||||
|
ik.valrange.min +
|
||||||
|
((da / Math.PI) * 0.75 + 0.5) *
|
||||||
|
(ik.valrange.max - ik.valrange.min);
|
||||||
|
el.setValue(dv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "circularrel":
|
||||||
|
if (da > ik.dragfrom.a + Math.PI) da -= Math.PI * 2;
|
||||||
|
if (da < ik.dragfrom.a - Math.PI) da += Math.PI * 2;
|
||||||
|
da -= ik.dragfrom.a;
|
||||||
|
dv = (da / Math.PI / 1.5) * (ik.valrange.max - ik.valrange.min);
|
||||||
|
if (ev.shiftKey) dv *= 0.2;
|
||||||
|
el.setValue(ik.dragfrom.v + dv);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "h":
|
||||||
|
case "v":
|
||||||
|
dv = (dx / ik.sensex - dy / ik.sensey) * (ik.valrange.max - ik.valrange.min);
|
||||||
|
if (ev.shiftKey) dv *= 0.2;
|
||||||
|
el.setValue(ik.dragfrom.v + dv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ik.pointerup = () => {
|
||||||
|
document.removeEventListener("mousemove", ik.pointermove);
|
||||||
|
document.removeEventListener("touchmove", ik.pointermove);
|
||||||
|
document.removeEventListener("mouseup", ik.pointerup);
|
||||||
|
document.removeEventListener("touchend", ik.pointerup);
|
||||||
|
document.removeEventListener("touchcancel", ik.pointerup);
|
||||||
|
document.removeEventListener("touchstart", ik.preventScroll);
|
||||||
|
let event = document.createEvent("HTMLEvents");
|
||||||
|
event.initEvent("change", false, true);
|
||||||
|
el.dispatchEvent(event);
|
||||||
|
};
|
||||||
|
ik.preventScroll = (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
};
|
||||||
|
ik.keydown = () => {
|
||||||
|
el.redraw();
|
||||||
|
};
|
||||||
|
ik.wheel = (ev) => {
|
||||||
|
let delta = ev.deltaY > 0 ? -ik.valrange.step : ik.valrange.step;
|
||||||
|
if (!ev.shiftKey) delta *= 5;
|
||||||
|
el.setValue(+el.value + delta);
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
};
|
||||||
|
el.redraw = (f) => {
|
||||||
|
if (f || ik.valueold != el.value) {
|
||||||
|
let v = (el.value - ik.valrange.min) / (ik.valrange.max - ik.valrange.min);
|
||||||
|
if (ik.sprites >= 1)
|
||||||
|
el.style.backgroundPosition =
|
||||||
|
"0px " + -((v * ik.sprites) | 0) * ik.frameheight + "px";
|
||||||
|
else {
|
||||||
|
switch (ik.itype) {
|
||||||
|
case "k":
|
||||||
|
el.style.transform = "rotate(" + (270 * v - 135) + "deg)";
|
||||||
|
break;
|
||||||
|
case "h":
|
||||||
|
el.style.backgroundPosition = (w - h) * v + "px 0px";
|
||||||
|
break;
|
||||||
|
case "v":
|
||||||
|
el.style.backgroundPosition = "0px " + (h - w) * (1 - v) + "px";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ik.valueold = el.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
el.refresh();
|
||||||
|
el.redraw(true);
|
||||||
|
el.addEventListener("keydown", ik.keydown);
|
||||||
|
el.addEventListener("mousedown", ik.pointerdown);
|
||||||
|
el.addEventListener("touchstart", ik.pointerdown);
|
||||||
|
el.addEventListener("wheel", ik.wheel);
|
||||||
|
};
|
||||||
|
let refreshque = () => {
|
||||||
|
let elem = document.querySelectorAll("input.input-knob,input.input-slider");
|
||||||
|
for (let i = 0; i < elem.length; ++i) procque.push([initKnobs, elem[i]]);
|
||||||
|
elem = document.querySelectorAll(
|
||||||
|
"input[type=checkbox].input-switch,input[type=radio].input-switch"
|
||||||
|
);
|
||||||
|
for (let i = 0; i < elem.length; ++i) {
|
||||||
|
procque.push([initSwitches, elem[i]]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let procque = [];
|
||||||
|
refreshque();
|
||||||
|
setInterval(() => {
|
||||||
|
for (let i = 0; procque.length > 0 && i < 8; ++i) {
|
||||||
|
let q = procque.shift();
|
||||||
|
q[0](q[1]);
|
||||||
|
}
|
||||||
|
if (procque.length <= 0) refreshque();
|
||||||
|
}, 50);
|
||||||
|
});
|
@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Module test</title>
|
||||||
|
<script src="{{url_for('static', filename='js/input-knobs.js')}}"></script>
|
||||||
|
<link rel="stylesheet" href="{{url_for('static', filename='css/style.css')}}" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul>
|
||||||
|
{% for module in modules%}
|
||||||
|
<li>{{module}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<form class="module" method="POST">
|
||||||
|
<div class="control">
|
||||||
|
<input type="range" class="input-knob" name="attack" data-fgcolor="white" />
|
||||||
|
<label for="attack">Attack</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control">
|
||||||
|
<input type="range" class="input-knob" name="decay" data-fgcolor="white" />
|
||||||
|
<label for="decay">Decay</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control">
|
||||||
|
<input type="range" class="input-knob" name="sustain" data-fgcolor="white" />
|
||||||
|
<label for="sustain">Sustain</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control">
|
||||||
|
<input type="range" class="input-knob" name="release" data-fgcolor="white" />
|
||||||
|
<label for="release">Release</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="text" name="name" id="name" placeholder="Name" />
|
||||||
|
<input type="submit" id="submit" value="Save" />
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1 @@
|
|||||||
|
{"attack": "62", "decay": "17", "sustain": "78", "release": "100", "name": "A long pad"}
|
@ -0,0 +1 @@
|
|||||||
|
{"attack": "50", "decay": "50", "sustain": "50", "release": "50", "name": "AHahah"}
|
@ -0,0 +1 @@
|
|||||||
|
{"attack": "0", "decay": "0", "sustain": "14", "release": "20", "name": "a short stab"}
|
@ -0,0 +1 @@
|
|||||||
|
{"attack": "76", "decay": "50", "sustain": "31", "release": "73", "name": "af"}
|
Loading…
Reference in New Issue