| Server IP : 202.61.199.114 / Your IP : 216.73.217.139 Web Server : nginx/1.22.1 System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64 User : web20 ( 1018) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/arni-solutions.de/web/terminal/assets/ |
Upload File : |
/**
* PatternPad – PIN-Eingabe per Muster (ziehen) ODER Antippen einer Ziffer.
* Layout wie ein Telefon-Tastenfeld: 1–9 im 3×3-Raster, 0 mittig darunter.
* Liefert eine Ziffernfolge (0–9). Innerhalb eines Striches wird kein Knoten
* doppelt belegt; über mehrere Striche/Tipps hinweg dürfen Ziffern wiederholt
* werden (so sind 4–12 Stellen möglich).
*
* var pad = PatternPad.create(el, { min:4, max:12, onChange:function(value,len){} });
* pad.value(); pad.clear();
*/
(function (global) {
var NS = 'http://www.w3.org/2000/svg';
var LAYOUT = [
{ d: 1, r: 0, c: 0 }, { d: 2, r: 0, c: 1 }, { d: 3, r: 0, c: 2 },
{ d: 4, r: 1, c: 0 }, { d: 5, r: 1, c: 1 }, { d: 6, r: 1, c: 2 },
{ d: 7, r: 2, c: 0 }, { d: 8, r: 2, c: 1 }, { d: 9, r: 2, c: 2 },
{ d: 0, r: 3, c: 1 }
];
var ROWS = 4, CELL = 100;
function cx(n) { return 50 + n.c * CELL; }
function cy(n) { return 50 + n.r * CELL; }
function PatternPad(el, opts) {
opts = opts || {};
this.el = el;
this.min = opts.min || 4;
this.max = opts.max || 12;
this.onChange = opts.onChange || function () {};
this.seq = []; // Ziffern als Strings '0'..'9'
this.segments = []; // [fromIdx,toIdx] gezeichnete Linien (bleiben „haften")
this.stroke = []; // Knoten-Indizes im aktuellen Strich
this.drawing = false;
this._build();
}
PatternPad.prototype._build = function () {
var el = this.el;
el.classList.add('ppad');
el.innerHTML = '';
this.ind = document.createElement('div');
this.ind.className = 'ppad-ind';
el.appendChild(this.ind);
this.grid = document.createElement('div');
this.grid.className = 'ppad-grid';
this.svg = document.createElementNS(NS, 'svg');
this.svg.setAttribute('class', 'ppad-lines');
this.svg.setAttribute('viewBox', '0 0 300 ' + (ROWS * CELL));
this.svg.setAttribute('preserveAspectRatio', 'none');
this.grid.appendChild(this.svg);
this.nodes = LAYOUT.map(function (def, idx) {
var n = document.createElement('button');
n.type = 'button';
n.className = 'ppad-node';
n.setAttribute('data-idx', idx);
n.textContent = String(def.d);
n.style.gridColumn = String(def.c + 1);
n.style.gridRow = String(def.r + 1);
return { el: n, d: def.d, r: def.r, c: def.c, idx: idx };
});
this.nodes.forEach(function (nd) { this.grid.appendChild(nd.el); }, this);
el.appendChild(this.grid);
var ctr = document.createElement('div');
ctr.className = 'ppad-ctr';
this.clearBtn = document.createElement('button');
this.clearBtn.type = 'button';
this.clearBtn.className = 'ppad-clear';
this.clearBtn.textContent = 'Löschen';
ctr.appendChild(this.clearBtn);
el.appendChild(ctr);
this._bind();
this._render();
};
PatternPad.prototype._nodeAt = function (clientX, clientY) {
var t = document.elementFromPoint(clientX, clientY);
if (!t) return -1;
var n = t.closest ? t.closest('.ppad-node') : null;
if (!n || n.parentNode !== this.grid) return -1;
return parseInt(n.getAttribute('data-idx'), 10);
};
PatternPad.prototype._add = function (idx) {
if (idx < 0 || idx >= this.nodes.length) return;
if (this.seq.length >= this.max) return;
if (this.stroke.indexOf(idx) > -1) return; // im selben Strich nicht doppelt
if (this.stroke.length > 0) this.segments.push([this.stroke[this.stroke.length - 1], idx]);
this.stroke.push(idx);
this.seq.push(String(this.nodes[idx].d));
this._render();
this.onChange(this.value(), this.seq.length);
};
PatternPad.prototype._bind = function () {
var self = this;
var live = null;
function toView(clientX, clientY) {
var r = self.grid.getBoundingClientRect();
return [(clientX - r.left) / r.width * 300, (clientY - r.top) / r.height * (ROWS * CELL)];
}
function down(e) {
var idx = self._nodeAt(e.clientX, e.clientY);
if (idx < 0) return;
e.preventDefault();
self.drawing = true;
self.stroke = [];
self._add(idx);
live = document.createElementNS(NS, 'line');
live.setAttribute('class', 'ppad-live');
live.setAttribute('stroke', 'var(--ppad-line)');
live.setAttribute('stroke-width', '6');
live.setAttribute('stroke-linecap', 'round');
live.setAttribute('opacity', '0.6');
self.svg.appendChild(live);
window.addEventListener('pointermove', move);
window.addEventListener('pointerup', up);
}
function move(e) {
if (!self.drawing) return;
var idx = self._nodeAt(e.clientX, e.clientY);
if (idx > -1) self._add(idx);
if (live && self.stroke.length) {
var last = self.nodes[self.stroke[self.stroke.length - 1]];
var p = toView(e.clientX, e.clientY);
live.setAttribute('x1', cx(last)); live.setAttribute('y1', cy(last));
live.setAttribute('x2', p[0]); live.setAttribute('y2', p[1]);
}
}
function up() {
self.drawing = false;
if (live && live.parentNode) live.parentNode.removeChild(live);
live = null;
window.removeEventListener('pointermove', move);
window.removeEventListener('pointerup', up);
}
this.grid.addEventListener('pointerdown', down);
this.clearBtn.addEventListener('click', function () { self.clear(); });
};
PatternPad.prototype._render = function () {
var used = {};
this.seq.forEach(function (d) { used[d] = true; });
this.nodes.forEach(function (nd) { nd.el.classList.toggle('on', !!used[String(nd.d)]); });
while (this.svg.firstChild) this.svg.removeChild(this.svg.firstChild);
for (var s = 0; s < this.segments.length; s++) {
var a = this.nodes[this.segments[s][0]], b = this.nodes[this.segments[s][1]];
var ln = document.createElementNS(NS, 'line');
ln.setAttribute('x1', cx(a)); ln.setAttribute('y1', cy(a));
ln.setAttribute('x2', cx(b)); ln.setAttribute('y2', cy(b));
ln.setAttribute('stroke', 'var(--ppad-line)');
ln.setAttribute('stroke-width', '6');
ln.setAttribute('stroke-linecap', 'round');
ln.setAttribute('stroke-linejoin', 'round');
this.svg.appendChild(ln);
}
var dots = '';
for (var d = 0; d < this.seq.length; d++) dots += '<span class="ppad-dot"></span>';
this.ind.innerHTML = dots;
};
PatternPad.prototype.value = function () { return this.seq.join(''); };
PatternPad.prototype.length = function () { return this.seq.length; };
PatternPad.prototype.clear = function () {
this.seq = []; this.segments = []; this.stroke = [];
this._render();
this.onChange('', 0);
};
PatternPad.create = function (el, opts) { return new PatternPad(el, opts); };
global.PatternPad = PatternPad;
})(window);