/* ============================================================
   GLOBAL TARİH & SAAT SEÇİCİ — datetime-picker.js
   ------------------------------------------------------------
   Sayfadaki her <input type="date">, <input type="time"> ve
   <input type="datetime-local"> alanı otomatik olarak Türkçe,
   klavyeyle yazılabilir ve özel takvim/saat panelli bir alana
   dönüştürülür. Sonradan DOM'a eklenen alanlar MutationObserver
   ile yakalanır; hiçbir sayfada ayrı kod yazmak gerekmez.

   TASARIM KARARI — özgün input DOM'da kalır
   ------------------------------------------
   Özgün input silinmez, yalnızca görünmez hale getirilir ve
   ISO değerin (Y-m-d / H:i / Y-m-d\TH:i) tek kaynağı olarak
   kalır. name / id / required / min / max hep onun üzerindedir.
   Böylece:
     • form gönderimi ve sunucu tarafı hiç değişmez,
     • document.getElementById(...).value ISO döner,
     • $('input[type=date]') gibi mevcut seçiciler çalışmayı sürdürür,
     • .value = '2026-01-01' ataması (property ele geçirildiği için)
       görünen alanı da anında günceller.
   Görünen alan özgün input'un class ve style'ını devraldığından
   sayfaların kendi form stilleri bozulmaz.

   Biçim: tarih  gg.aa.yyyy      (16.08.2026)
          saat   ss.dd           (14.36)
          ikisi  gg.aa.yyyy ss.dd
   Ayırıcıyı değiştirmek için aşağıdaki SEP sabitleri yeterlidir.
   ============================================================ */

(function () {
    'use strict';

    if (window.DTP) return;                       // iki kez yüklenmeye karşı

    /* ── Yerelleştirme ve biçim sabitleri ──────────────────── */

    var SEP_DATE = '.';                           // gg.aa.yyyy
    var SEP_TIME = '.';                           // ss.dd
    var SEP_BOTH = ' ';                           // tarih ile saat arası

    var AYLAR = ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran',
                 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'];
    var AYLAR_KISA = ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz',
                      'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'];
    var GUNLER = ['Pt', 'Sa', 'Ça', 'Pe', 'Cu', 'Ct', 'Pz'];   // hafta pazartesi başlar

    var PH = { date: 'gg' + SEP_DATE + 'aa' + SEP_DATE + 'yyyy',
               time: 'ss' + SEP_TIME + 'dd',
               datetime: 'gg' + SEP_DATE + 'aa' + SEP_DATE + 'yyyy' + SEP_BOTH + 'ss' + SEP_TIME + 'dd' };

    var LEN = { date: 8, time: 4, datetime: 12 };  // maskeye sığan rakam adedi

    var SVG_PREV = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 5l-7 7 7 7"/></svg>';
    var SVG_NEXT = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5l7 7-7 7"/></svg>';

    /* ── Küçük yardımcılar ─────────────────────────────────── */

    function pad(n) { return (n < 10 ? '0' : '') + n; }
    function digits(s) { return (s || '').replace(/\D+/g, ''); }
    function clampInt(v, lo, hi) { return v < lo ? lo : (v > hi ? hi : v); }
    function daysInMonth(y, m) { return new Date(y, m + 1, 0).getDate(); }
    function isRealDate(y, m, d) {
        return m >= 0 && m <= 11 && d >= 1 && d <= daysInMonth(y, m) && y >= 1000 && y <= 9999;
    }
    /* Haftanın gününü pazartesi = 0 olacak şekilde döndürür. */
    function wd(date) { return (date.getDay() + 6) % 7; }

    function el(tag, cls, html) {
        var n = document.createElement(tag);
        if (cls) n.className = cls;
        if (html != null) n.innerHTML = html;
        return n;
    }

    /* ── ISO ↔ parça dönüşümleri ───────────────────────────── */

    /** ISO değerden {y,m,d,hh,mi} çıkarır; boş/bozuksa null. */
    function fromIso(iso, mode) {
        if (!iso) return null;
        var mt;
        if (mode === 'time') {
            mt = /^(\d{1,2}):(\d{2})/.exec(iso);
            if (!mt) return null;
            return { y: null, m: null, d: null, hh: clampInt(+mt[1], 0, 23), mi: clampInt(+mt[2], 0, 59) };
        }
        mt = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{1,2}):(\d{2}))?/.exec(iso);
        if (!mt) return null;
        var y = +mt[1], m = +mt[2] - 1, d = +mt[3];
        if (!isRealDate(y, m, d)) return null;
        return { y: y, m: m, d: d, hh: mt[4] != null ? clampInt(+mt[4], 0, 23) : 0, mi: mt[5] != null ? clampInt(+mt[5], 0, 59) : 0 };
    }

    /** {y,m,d,hh,mi} → ISO metin (input.value'ya yazılan biçim). */
    function toIso(p, mode) {
        if (!p) return '';
        if (mode === 'time') return pad(p.hh) + ':' + pad(p.mi);
        var dt = p.y + '-' + pad(p.m + 1) + '-' + pad(p.d);
        return mode === 'datetime' ? dt + 'T' + pad(p.hh) + ':' + pad(p.mi) : dt;
    }

    /** {y,m,d,hh,mi} → ekranda görünen Türkçe biçim. */
    function toText(p, mode) {
        if (!p) return '';
        var t = pad(p.hh) + SEP_TIME + pad(p.mi);
        if (mode === 'time') return t;
        var d = pad(p.d) + SEP_DATE + pad(p.m + 1) + SEP_DATE + p.y;
        return mode === 'datetime' ? d + SEP_BOTH + t : d;
    }

    /** Kullanıcının yazdığı rakamları maskeli metne çevirir (kısmi girişte de çalışır). */
    function mask(dg, mode) {
        var out = '';
        if (mode === 'time') {
            out = dg.slice(0, 2);
            if (dg.length > 2) out += SEP_TIME + dg.slice(2, 4);
            return out;
        }
        out = dg.slice(0, 2);
        if (dg.length > 2) out += SEP_DATE + dg.slice(2, 4);
        if (dg.length > 4) out += SEP_DATE + dg.slice(4, 8);
        if (mode === 'datetime' && dg.length > 8) {
            out += SEP_BOTH + dg.slice(8, 10);
            if (dg.length > 10) out += SEP_TIME + dg.slice(10, 12);
        }
        return out;
    }

    /**
     * Yazılan rakamları tarihe/saate çözer. Eksik yazımı akıllıca tamamlar:
     *   "9"        → 09.00      (saat)
     *   "1608"     → 16.08.<bu yıl>
     *   "160826"   → 16.08.2026
     * Çözülemezse null döner (alan kırmızıya döner, odak çıkınca temizlenir).
     */
    function parseDigits(dg, mode, ref) {
        var now = ref || new Date();
        if (!dg) return null;

        if (mode === 'time') {
            if (dg.length === 1) return { y: null, m: null, d: null, hh: +dg, mi: 0 };
            var hh = +dg.slice(0, 2);
            var mi = dg.length > 2 ? +(dg.slice(2, 4).length === 1 ? dg.slice(2, 3) + '0' : dg.slice(2, 4)) : 0;
            if (hh > 23 || mi > 59) return null;
            return { y: null, m: null, d: null, hh: hh, mi: mi };
        }

        if (dg.length < 4) return null;
        var d = +dg.slice(0, 2), m = +dg.slice(2, 4) - 1, ys = dg.slice(4, 8), y;
        if (ys.length === 0) y = now.getFullYear();
        else if (ys.length <= 2) y = 2000 + +(ys.length === 1 ? ys + '0' : ys);
        else if (ys.length === 3) return null;
        else y = +ys;
        if (!isRealDate(y, m, d)) return null;

        var H = 0, M = 0;
        if (mode === 'datetime' && dg.length > 8) {
            H = +dg.slice(8, 10);
            var ms = dg.slice(10, 12);
            M = ms.length ? +(ms.length === 1 ? ms + '0' : ms) : 0;
            if (H > 23 || M > 59) return null;
        }
        return { y: y, m: m, d: d, hh: H, mi: M };
    }

    /* ── Alan sınırları (min / max) ────────────────────────── */

    function bound(p, mode) {
        if (!p) return null;
        if (mode === 'time') return p.hh * 60 + p.mi;
        return ((p.y * 12 + p.m) * 31 + p.d) * 1440 + p.hh * 60 + p.mi;
    }

    /* ── Örnek (bir input = bir Field) ─────────────────────── */

    var uid = 0;
    var open = null;                              // açık olan panel (tekil)

    function Field(native) {
        var self = this;
        var t = native.getAttribute('type');
        this.mode = t === 'date' ? 'date' : (t === 'time' ? 'time' : 'datetime');
        this.native = native;
        this.id = ++uid;
        this.silent = false;

        /* Görünen alan: özgün class ve style devralınır ki sayfanın
           kendi form stili birebir korunsun. Özgün değerler native'de
           saklanır; böylece sarılmış bir satır cloneNode ile çoğaltılsa
           bile kopya doğru class ile yeniden kurulur. */
        var cls = native.getAttribute('data-dtp-cls');
        var sty = native.getAttribute('data-dtp-sty');
        if (cls === null) { cls = native.className; native.setAttribute('data-dtp-cls', cls); }
        if (sty === null) { sty = native.getAttribute('style') || ''; native.setAttribute('data-dtp-sty', sty); }

        var vis = el('input');
        vis.type = 'text';
        vis.className = cls;
        if (sty) vis.setAttribute('style', sty);
        vis.setAttribute('data-dtp', this.mode);
        vis.setAttribute('autocomplete', 'off');
        vis.setAttribute('inputmode', 'numeric');
        vis.setAttribute('placeholder', native.getAttribute('placeholder') || PH[this.mode]);
        if (native.getAttribute('title')) vis.setAttribute('title', native.getAttribute('title'));
        if (native.getAttribute('tabindex')) vis.setAttribute('tabindex', native.getAttribute('tabindex'));
        if (native.getAttribute('aria-label')) vis.setAttribute('aria-label', native.getAttribute('aria-label'));
        vis.disabled = native.disabled;
        vis.readOnly = native.readOnly;

        native.className = 'dtp-native';
        native.removeAttribute('style');
        native.setAttribute('tabindex', '-1');
        native.setAttribute('data-dtp-native', '1');
        native.parentNode.insertBefore(vis, native.nextSibling);

        this.vis = vis;
        native.__dtp = this;
        vis.__dtp = this;

        /* .value ataması görünen alanı da güncellesin */
        var proto = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
        try {
            Object.defineProperty(native, 'value', {
                configurable: true,
                get: function () { return proto.get.call(native); },
                set: function (v) { proto.set.call(native, v); if (!self.silent) self.pull(); }
            });
        } catch (e) { /* tarayıcı izin vermezse yalnızca attribute izlemeye düşer */ }

        /* setAttribute('value'), disabled, min/max değişimlerini izle */
        new MutationObserver(function () { self.pull(); self.mirror(); })
            .observe(native, { attributes: true, attributeFilter: ['value', 'min', 'max', 'step', 'disabled', 'readonly'] });

        /* form.reset() sonrası senkron */
        if (native.form) native.form.addEventListener('reset', function () { setTimeout(function () { self.pull(); }, 0); });

        /* label[for] tıklaması native'i odaklar → görünen alana aktar */
        native.addEventListener('focus', function () { if (!vis.disabled) vis.focus(); });
        native.addEventListener('invalid', function () { vis.classList.add('dtp-bad'); });

        vis.addEventListener('mousedown', function (e) {
            if (vis.disabled) return;
            if (!open || open.f !== self) { setTimeout(function () { self.open(); }, 0); }
            else if (e.target === vis && open) { /* açıkken tıklama alanı düzenlemeye bırakır */ }
        });
        vis.addEventListener('input', function () { self.onType(); });
        vis.addEventListener('keydown', function (e) { self.onKey(e); });
        vis.addEventListener('blur', function () { setTimeout(function () { self.commitText(true); }, 120); });
        vis.addEventListener('paste', function () { setTimeout(function () { self.onType(); }, 0); });

        this.pull();
    }

    /** ISO değeri okuyup görünen metni tazeler. */
    Field.prototype.pull = function () {
        var p = fromIso(this.native.value, this.mode);
        this.vis.value = toText(p, this.mode);
        this.vis.classList.remove('dtp-bad');
        this.parts = p;
    };

    /** disabled / readonly durumunu görünen alana yansıtır. */
    Field.prototype.mirror = function () {
        this.vis.disabled = this.native.disabled;
        this.vis.readOnly = this.native.readOnly;
    };

    /** ISO değeri yazar ve sayfanın dinlediği input/change olaylarını tetikler. */
    Field.prototype.set = function (p, fire) {
        var iso = toIso(p, this.mode);
        if (iso === this.native.value) { this.pull(); return; }
        this.silent = true;
        this.native.value = iso;
        this.silent = false;
        this.pull();
        if (fire !== false) {
            this.native.dispatchEvent(new Event('input', { bubbles: true }));
            this.native.dispatchEvent(new Event('change', { bubbles: true }));
        }
    };

    /** Yazarken maskeyi uygular, imleç konumunu korur. */
    Field.prototype.onType = function () {
        var v = this.vis, before = v.value.slice(0, v.selectionStart || 0);
        var dg = digits(v.value).slice(0, LEN[this.mode]);
        var keep = digits(before).length;
        var txt = mask(dg, this.mode);

        v.value = txt;
        var pos = 0, seen = 0;
        while (pos < txt.length && seen < keep) { if (/\d/.test(txt[pos])) seen++; pos++; }
        try { v.setSelectionRange(pos, pos); } catch (e) { /* yoksay */ }

        /* Yazım sürerken native değere dokunulmaz: değişiklik olayları tek
           seferde, kesinleştirme anında (blur / Enter / panel) atılsın. */
        var p = parseDigits(dg, this.mode);
        v.classList.toggle('dtp-bad', dg.length > 0 && !p);
        if (p) {
            this.parts = p;
            if (open && open.f === this) open.sync(p);
        }
    };

    /** Odak çıkışında/Enter'da yazılanı kesinleştirir. */
    Field.prototype.commitText = function (onBlur) {
        if (open && open.f === this && onBlur && open.root.contains(document.activeElement)) return;
        var dg = digits(this.vis.value).slice(0, LEN[this.mode]);
        if (!dg) { if (this.native.value) this.set(null); else this.pull(); return; }
        var p = parseDigits(dg, this.mode);
        if (!p) { this.pull(); return; }             // çözülemedi → eski değere dön
        this.set(this.limit(p));
    };

    /** min / max sınırlarına kırpar. */
    Field.prototype.limit = function (p) {
        var lo = fromIso(this.native.getAttribute('min'), this.mode);
        var hi = fromIso(this.native.getAttribute('max'), this.mode);
        if (lo && bound(p, this.mode) < bound(lo, this.mode)) return lo;
        if (hi && bound(p, this.mode) > bound(hi, this.mode)) return hi;
        return p;
    };

    Field.prototype.onKey = function (e) {
        var k = e.key;
        if (k === 'Escape') { if (open && open.f === this) { close(); e.stopPropagation(); } return; }
        if (k === 'Enter') {
            this.commitText(false);
            if (open && open.f === this) { close(); e.preventDefault(); }
            return;
        }
        if (k === 'Tab') { this.commitText(false); close(); return; }
        if (!open || open.f !== this) {
            if (k === 'ArrowDown' || k === 'F4') { e.preventDefault(); this.open(); }
            return;
        }
        open.key(e);
    };

    Field.prototype.open = function () {
        if (this.vis.disabled) return;
        if (open && open.f === this) return;
        close();
        open = new Pop(this);
    };

    /* ── Açılır panel ──────────────────────────────────────── */

    function close() {
        if (!open) return;
        var p = open; open = null;
        p.root.classList.add('dtp-closing');
        p.off();
        setTimeout(function () { if (p.root.parentNode) p.root.parentNode.removeChild(p.root); }, 120);
    }

    function Pop(f) {
        var self = this;
        this.f = f;
        this.mode = f.mode;
        this.step = minuteStep(f.native);

        var now = new Date();
        var p = f.parts || fromIso(f.native.value, f.mode);
        this.p = p ? { y: p.y, m: p.m, d: p.d, hh: p.hh, mi: p.mi } : null;
        this.view = p && p.y != null ? { y: p.y, m: p.m } : { y: now.getFullYear(), m: now.getMonth() };
        this.cur = p && p.d != null ? new Date(p.y, p.m, p.d) : new Date(now.getFullYear(), now.getMonth(), now.getDate());
        this.pane = null;                          // 'm' | 'y' | null

        var root = el('div', 'dtp-pop' + (this.mode === 'time' ? ' only-time' : ''));
        root.setAttribute('role', 'dialog');
        var wrap = el('div', 'dtp-wrap');
        var row = el('div', 'dtp-row');

        if (this.mode !== 'time') { this.buildCal(row); }
        if (this.mode !== 'date') { this.buildTime(row); }

        wrap.appendChild(row);
        wrap.appendChild(this.buildFoot());
        root.appendChild(wrap);
        document.body.appendChild(root);
        this.root = root;

        /* Panel içine tıklarken alanın odağı kaybolmasın */
        root.addEventListener('mousedown', function (e) { e.preventDefault(); });

        this.render();
        this.place();

        this._out = function (e) {
            if (root.contains(e.target) || e.target === f.vis) return;
            f.commitText(false); close();
        };
        this._re = function () { self.place(); };
        document.addEventListener('mousedown', this._out, true);
        window.addEventListener('resize', this._re);
        window.addEventListener('scroll', this._re, true);
    }

    Pop.prototype.off = function () {
        document.removeEventListener('mousedown', this._out, true);
        window.removeEventListener('resize', this._re);
        window.removeEventListener('scroll', this._re, true);
    };

    function minuteStep(native) {
        var s = parseInt(native.getAttribute('step'), 10);
        if (!s || s < 60) return 1;
        var m = Math.round(s / 60);
        return m > 0 && m < 60 ? m : 1;
    }

    /* Takvim iskeleti */
    Pop.prototype.buildCal = function (row) {
        var self = this;
        var cal = el('div', 'dtp-cal');

        var head = el('div', 'dtp-head');
        var prev = el('button', 'dtp-nav', SVG_PREV); prev.type = 'button'; prev.setAttribute('aria-label', 'Önceki ay');
        var next = el('button', 'dtp-nav', SVG_NEXT); next.type = 'button'; next.setAttribute('aria-label', 'Sonraki ay');
        var title = el('div', 'dtp-title');
        var bm = el('button', 'dtp-t'); bm.type = 'button';
        var by = el('button', 'dtp-t'); by.type = 'button';
        title.appendChild(bm); title.appendChild(by);
        head.appendChild(prev); head.appendChild(title); head.appendChild(next);

        prev.addEventListener('click', function () { self.shift(-1); });
        next.addEventListener('click', function () { self.shift(1); });
        bm.addEventListener('click', function () { self.togglePane('m'); });
        by.addEventListener('click', function () { self.togglePane('y'); });

        var wdr = el('div', 'dtp-wd');
        GUNLER.forEach(function (g, i) {
            var s = el('span', i > 4 ? 'we' : null, g);
            wdr.appendChild(s);
        });

        var grid = el('div', 'dtp-grid');
        grid.addEventListener('click', function (e) {
            var b = e.target.closest('.dtp-day');
            if (!b || b.disabled) return;
            self.pickDay(+b.dataset.y, +b.dataset.m, +b.dataset.d);
        });

        cal.appendChild(head); cal.appendChild(wdr); cal.appendChild(grid);
        row.appendChild(cal);
        this.cal = cal; this.grid = grid; this.bm = bm; this.by = by; this.prev = prev; this.next = next;
    };

    /* Saat/dakika kolonları */
    Pop.prototype.buildTime = function (row) {
        var self = this;
        var box = el('div', 'dtp-time');

        var clock = el('div', 'dtp-clock');
        var bh = el('b', null, '--'), bs = el('i', null, SEP_TIME), bmn = el('b', null, '--');
        clock.appendChild(bh); clock.appendChild(bs); clock.appendChild(bmn);

        var lab = el('div', 'dtp-tlab', '<span>Saat</span><span>Dakika</span>');
        var cols = el('div', 'dtp-cols');
        var ch = el('div', 'dtp-col'), cm = el('div', 'dtp-col');

        for (var h = 0; h < 24; h++) ch.appendChild(tcell(h, 'h'));
        for (var m = 0; m < 60; m += this.step) cm.appendChild(tcell(m, 'm'));

        function tcell(v, kind) {
            var b = el('button', 'dtp-tc', pad(v));
            b.type = 'button'; b.dataset.v = v; b.dataset.k = kind;
            return b;
        }
        cols.appendChild(ch); cols.appendChild(cm);
        cols.addEventListener('click', function (e) {
            var b = e.target.closest('.dtp-tc');
            if (!b) return;
            self.pickTime(b.dataset.k, +b.dataset.v);
        });

        box.appendChild(clock); box.appendChild(lab); box.appendChild(cols);
        row.appendChild(box);
        this.ch = ch; this.cm = cm; this.bh = bh; this.bmn = bmn;
    };

    Pop.prototype.buildFoot = function () {
        var self = this, foot = el('div', 'dtp-foot');

        var clr = el('button', 'dtp-btn dgr', 'Temizle'); clr.type = 'button';
        clr.addEventListener('click', function () { self.f.set(null); close(); self.f.vis.focus(); });
        foot.appendChild(clr);
        foot.appendChild(el('span', 'dtp-sp'));

        var nowLbl = this.mode === 'date' ? 'Bugün' : 'Şimdi';
        var nowBtn = el('button', 'dtp-btn', nowLbl); nowBtn.type = 'button';
        nowBtn.addEventListener('click', function () {
            var n = new Date();
            var p = { y: n.getFullYear(), m: n.getMonth(), d: n.getDate(), hh: n.getHours(), mi: n.getMinutes() };
            if (self.mode === 'date') { p.hh = 0; p.mi = 0; }
            p = self.f.limit(p);
            self.p = p; self.view = { y: p.y, m: p.m }; self.cur = new Date(p.y, p.m, p.d);
            self.f.set(p);
            if (self.mode === 'date') { close(); self.f.vis.focus(); } else self.render();
        });
        foot.appendChild(nowBtn);

        if (this.mode !== 'date') {
            var ok = el('button', 'dtp-btn pri', 'Tamam'); ok.type = 'button';
            ok.addEventListener('click', function () { self.commit(); close(); self.f.vis.focus(); });
            foot.appendChild(ok);
        }
        return foot;
    };

    /* Panelin ekrandaki yeri — alan altına, sığmazsa üstüne */
    Pop.prototype.place = function () {
        var r = this.f.vis.getBoundingClientRect();
        var w = this.root.offsetWidth, h = this.root.offsetHeight;
        var vw = document.documentElement.clientWidth, vh = document.documentElement.clientHeight;
        var up = r.bottom + 6 + h > vh && r.top - 6 - h > 0;

        var left = clampInt(r.left, 8, Math.max(8, vw - w - 8));
        var top = up ? r.top - h - 6 : r.bottom + 6;
        this.root.classList.toggle('dtp-up', up);
        this.root.style.left = Math.round(left) + 'px';
        this.root.style.top = Math.round(top) + 'px';
    };

    Pop.prototype.shift = function (dir) {
        var m = this.view.m + dir, y = this.view.y;
        if (m < 0) { m = 11; y--; } else if (m > 11) { m = 0; y++; }
        this.view = { y: y, m: m };
        this.render();
    };

    Pop.prototype.togglePane = function (which) {
        this.pane = this.pane === which ? null : which;
        this.render();
    };

    /** Dışarıdan (klavyeyle yazarken) gelen değeri panele işler. */
    Pop.prototype.sync = function (p) {
        if (!p) return;
        this.p = { y: p.y, m: p.m, d: p.d, hh: p.hh, mi: p.mi };
        if (p.y != null) { this.view = { y: p.y, m: p.m }; this.cur = new Date(p.y, p.m, p.d); }
        this.render();
    };

    Pop.prototype.pickDay = function (y, m, d) {
        var p = this.p || { hh: 0, mi: 0 };
        this.p = { y: y, m: m, d: d, hh: p.hh || 0, mi: p.mi || 0 };
        this.view = { y: y, m: m };
        this.cur = new Date(y, m, d);
        this.pane = null;
        this.f.set(this.f.limit(this.p));
        if (this.mode === 'date') { close(); this.f.vis.focus(); }
        else this.render();
    };

    Pop.prototype.pickTime = function (kind, v) {
        var n = new Date();
        var p = this.p || (this.mode === 'time'
            ? { y: null, m: null, d: null, hh: 0, mi: 0 }
            : { y: n.getFullYear(), m: n.getMonth(), d: n.getDate(), hh: 0, mi: 0 });
        if (kind === 'h') p.hh = v; else p.mi = v;
        this.p = p;
        this.f.set(this.f.limit(p));
        this.render();
    };

    Pop.prototype.commit = function () {
        if (this.p) this.f.set(this.f.limit(this.p));
    };

    /* ── Çizim ─────────────────────────────────────────────── */

    Pop.prototype.render = function () {
        if (this.grid) this.renderCal();
        if (this.ch) this.renderTime();
    };

    Pop.prototype.renderCal = function () {
        var v = this.view, sel = this.p, self = this;
        var lo = fromIso(this.f.native.getAttribute('min'), this.mode);
        var hi = fromIso(this.f.native.getAttribute('max'), this.mode);
        var today = new Date(); today.setHours(0, 0, 0, 0);

        this.bm.innerHTML = AYLAR[v.m];
        this.by.innerHTML = v.y;
        this.bm.classList.toggle('on', this.pane === 'm');
        this.by.classList.toggle('on', this.pane === 'y');

        var first = new Date(v.y, v.m, 1);
        var start = new Date(v.y, v.m, 1 - wd(first));
        var html = '';
        for (var i = 0; i < 42; i++) {
            var dt = new Date(start.getFullYear(), start.getMonth(), start.getDate() + i);
            var y = dt.getFullYear(), m = dt.getMonth(), d = dt.getDate();
            var cls = 'dtp-day';
            if (m !== v.m) cls += ' out';
            if (wd(dt) > 4) cls += ' we';
            if (dt.getTime() === today.getTime()) cls += ' today';
            if (sel && sel.y === y && sel.m === m && sel.d === d) cls += ' sel';
            if (this.cur && dt.getTime() === new Date(this.cur.getFullYear(), this.cur.getMonth(), this.cur.getDate()).getTime()) cls += ' kb';
            var bad = (lo && dayNum(y, m, d) < dayNum(lo.y, lo.m, lo.d)) ||
                      (hi && dayNum(y, m, d) > dayNum(hi.y, hi.m, hi.d));
            html += '<button type="button" class="' + cls + '" data-y="' + y + '" data-m="' + m + '" data-d="' + d + '"' +
                    (bad ? ' disabled' : '') + '>' + d + '</button>';
        }
        this.grid.innerHTML = html;

        /* Ay / yıl katmanı */
        var oldPane = this.cal.querySelector('.dtp-pane');
        if (oldPane) oldPane.parentNode.removeChild(oldPane);
        if (this.pane === 'm') {
            var pm = el('div', 'dtp-pane m');
            AYLAR_KISA.forEach(function (a, i) {
                var b = el('button', i === v.m ? 'sel' : null, a); b.type = 'button';
                b.addEventListener('click', function () { self.view = { y: v.y, m: i }; self.pane = null; self.render(); });
                pm.appendChild(b);
            });
            this.cal.appendChild(pm);
        } else if (this.pane === 'y') {
            var py = el('div', 'dtp-pane y');
            var y0 = (lo ? lo.y : v.y - 70), y1 = (hi ? hi.y : v.y + 20);
            if (y1 - y0 > 160) { y0 = v.y - 70; y1 = v.y + 20; }
            var mark = null;
            for (var yy = y0; yy <= y1; yy++) {
                (function (yv) {
                    var b = el('button', yv === v.y ? 'sel' : null, yv); b.type = 'button';
                    b.addEventListener('click', function () { self.view = { y: yv, m: v.m }; self.pane = 'm'; self.render(); });
                    if (yv === v.y) mark = b;
                    py.appendChild(b);
                })(yy);
            }
            this.cal.appendChild(py);
            if (mark) py.scrollTop = Math.max(0, mark.offsetTop - py.clientHeight / 2 + 18);
        }
    };

    function dayNum(y, m, d) { return (y * 12 + m) * 31 + d; }

    Pop.prototype.renderTime = function () {
        var p = this.p;
        var hh = p ? p.hh : null, mi = p ? p.mi : null;
        this.bh.textContent = hh == null ? '--' : pad(hh);
        this.bmn.textContent = mi == null ? '--' : pad(mi);

        paintCol(this.ch, hh);
        paintCol(this.cm, mi);

        function paintCol(col, val) {
            var hit = null;
            Array.prototype.forEach.call(col.children, function (b) {
                var on = val != null && +b.dataset.v === val;
                b.classList.toggle('sel', on);
                if (on) hit = b;
            });
            if (hit) {
                var goal = hit.offsetTop - (col.clientHeight - hit.offsetHeight) / 2;
                if (Math.abs(col.scrollTop - goal) > 2) col.scrollTop = goal;
            }
        }
    };

    /* Klavye: panel açıkken ok tuşları takvimde gezinir */
    Pop.prototype.key = function (e) {
        var k = e.key, step = 0;
        if (this.mode === 'time') {
            if (k === 'ArrowUp' || k === 'ArrowDown') {
                e.preventDefault();
                var p = this.p || { hh: new Date().getHours(), mi: 0, y: null, m: null, d: null };
                p.mi = (p.mi + (k === 'ArrowUp' ? this.step : -this.step) + 60) % 60;
                this.p = p; this.f.set(this.f.limit(p)); this.render();
            }
            return;
        }
        if (k === 'ArrowLeft') step = -1;
        else if (k === 'ArrowRight') step = 1;
        else if (k === 'ArrowUp') step = -7;
        else if (k === 'ArrowDown') step = 7;
        else if (k === 'PageUp') { e.preventDefault(); this.shift(-1); return; }
        else if (k === 'PageDown') { e.preventDefault(); this.shift(1); return; }
        else return;

        e.preventDefault();
        var c = this.cur;
        this.cur = new Date(c.getFullYear(), c.getMonth(), c.getDate() + step);
        this.view = { y: this.cur.getFullYear(), m: this.cur.getMonth() };
        this.renderCal();
    };

    /* ── Otomatik dönüştürme ───────────────────────────────── */

    var SEL = 'input[type="date"],input[type="time"],input[type="datetime-local"]';

    function upgrade(n) {
        if (!n || n.__dtp || !n.parentNode) return;
        if (n.hasAttribute('data-dtp-skip') || (n.closest && n.closest('[data-dtp-skip]'))) return;
        /* cloneNode ile çoğaltılmış bir alanın sahipsiz görünen eşi varsa
           at; yerine yenisi kurulacak. */
        var stale = n.nextElementSibling;
        if (stale && stale.hasAttribute('data-dtp') && !stale.__dtp) stale.parentNode.removeChild(stale);
        try { new Field(n); } catch (e) { if (window.console) console.warn('[DTP]', e); }
    }

    function enhance(root) {
        var scope = root || document;
        if (scope.nodeType === 1 && scope.matches && scope.matches(SEL)) upgrade(scope);
        if (!scope.querySelectorAll) return;
        Array.prototype.forEach.call(scope.querySelectorAll(SEL), upgrade);
    }

    function boot() {
        enhance(document);
        new MutationObserver(function (muts) {
            for (var i = 0; i < muts.length; i++) {
                var added = muts[i].addedNodes;
                for (var j = 0; j < added.length; j++) {
                    var n = added[j];
                    if (n.nodeType !== 1) continue;
                    if (n.matches && n.matches(SEL)) upgrade(n);
                    else if (n.querySelector && n.querySelector(SEL)) enhance(n);
                }
            }
        }).observe(document.documentElement, { childList: true, subtree: true });
    }

    window.DTP = {
        enhance: enhance,
        /**
         * Açık paneli kapatır. Bir input verilirse YALNIZCA o alanın paneli
         * kapanır; satır içi düzenleyiciler kendi alanını kapatırken başka bir
         * alanın açık takvimini yanlışlıkla kapatmasın diye ayrım gerekiyor.
         */
        close: function (input) {
            if (input && (!open || open.f !== input.__dtp)) return;
            close();
        },
        /** Bir input'un görünen metnini ISO değerine göre tazeler. */
        refresh: function (input) { if (input && input.__dtp) input.__dtp.pull(); },
        /**
         * Görünen alana ELLE yazılmış metni ISO'ya çevirip özgün input'a işler
         * ve ISO değeri döndürür.
         *
         * Yazım normalde yalnızca odak çıkışında / Enter'da kesinleşir. Satır
         * içi düzenleyiciler (ledger) değeri o anı bekleyemez: Tab ile sonraki
         * alana geçerken ya da odak kaybında değeri EŞZAMANLI okumak zorundalar.
         * Bu kanca olmadan "18.01.2001 yazıp Tab'a bas" senaryosunda alan hâlâ
         * eski ISO değeri taşıyor ve yazılan tarih sessizce kayboluyordu.
         */
        commit: function (input) {
            if (input && input.__dtp) input.__dtp.commitText(false);
            return input ? input.value : '';
        },
        /** Alanın ekranda görünen (yazılabilir) düğümü; sarılmamışsa kendisi. */
        visible: function (input) { return input && input.__dtp ? input.__dtp.vis : input; },
        /**
         * Düğüm açık takvim/saat panelinin içinde mi?
         * Panel document.body altına taşındığı için "odak alanın dışına çıktı"
         * denetimleri kendi başına yanılır; takvimden gün seçen kullanıcının
         * düzenlemesi kapanmasın diye bu denetim gerekli.
         */
        inPanel: function (node) { return !!(node && node.closest && node.closest('.dtp-pop')); },
        format: toText,
        parse: fromIso
    };

    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);
    else boot();
})();
