{"id":2807,"date":"2026-10-13T07:11:56","date_gmt":"2026-10-13T05:11:56","guid":{"rendered":"https:\/\/cad-kompass.de\/?p=2807"},"modified":"2026-10-13T07:11:56","modified_gmt":"2026-10-13T05:11:56","slug":"schluessel-check","status":"publish","type":"post","link":"https:\/\/cad-kompass.de\/?p=2807","title":{"rendered":"Schl\u00fcssel-Check"},"content":{"rendered":"\n<style>\n    .sk-box {\n        --sk-primary: #3f5f75;\n        --sk-primary-dark: #2c4a5e;\n        --sk-primary-deep: #1e3548;\n        --sk-good: #059669;\n        --sk-good-deep: #15803d;\n        --sk-warn: #d97706;\n        --sk-ink: #1e293b;\n        --sk-text: #334155;\n        --sk-muted: #64748b;\n        --sk-border: #c8d6e0;\n        --sk-hair: #e2e8f0;\n        --sk-surface: #f7f9fb;\n        --sk-mono: ui-monospace, \"SF Mono\", Menlo, Consolas, monospace;\n        background: #fff;\n        border: 3px solid var(--sk-primary);\n        border-radius: 8px;\n        margin: 28px 0;\n        overflow: hidden;\n        position: relative;\n        font-family: inherit;\n        color: var(--sk-text);\n        line-height: 1.55;\n        text-align: left;\n    }\n    .sk-box, .sk-box *, .sk-box *::before, .sk-box *::after { box-sizing: border-box; }\n\n    .sk-header {\n        background: linear-gradient(135deg, var(--sk-primary-deep) 0%, var(--sk-primary-dark) 100%);\n        padding: 13px 28px 12px;\n        position: relative;\n    }\n    .sk-header::after {\n        content: '';\n        position: absolute;\n        left: 0; right: 0; bottom: 0;\n        height: 3px;\n        background: repeating-linear-gradient(90deg, var(--sk-primary) 0px, var(--sk-primary) 8px, transparent 8px, transparent 12px);\n    }\n    .sk-header-top { display: flex; align-items: center; gap: 14px; }\n    .sk-header-icon { width: 36px; height: 36px; flex-shrink: 0; }\n    .sk-box .sk-box-title { font-size: 1.15rem; color: #fff; font-weight: 700; margin: 0; padding: 0; letter-spacing: -0.01em; line-height: 1.3; border: 0; background: none; }\n    .sk-box .sk-box-desc { font-size: 0.85rem; color: rgba(255,255,255,0.78); margin: 4px 0 0; line-height: 1.5; }\n\n    .sk-body { padding: 12px 28px 16px; background: var(--sk-surface); }\n    .sk-privacy {\n        background: #f0fdf4;\n        border: 1px solid #bbf7d0;\n        border-radius: 3px;\n        padding: 6px 14px;\n        margin-bottom: 10px;\n        font-size: 0.8rem;\n        color: var(--sk-good-deep);\n        line-height: 1.5;\n        display: flex;\n        align-items: center;\n        gap: 8px;\n    }\n    .sk-privacy::before { content: '\\1F512'; flex-shrink: 0; font-size: 0.9rem; }\n\n    .sk-box p, .sk-box h2, .sk-box h3, .sk-box details { margin: 0; padding: 0; }\n    .sk-box h2, .sk-box h3 { font-family: inherit; text-transform: none; letter-spacing: 0; border: 0; background: none; }\n    .sk-box [hidden] { display: none !important; }\n\n    \/* ---------- Eingabe ---------- *\/\n    .sk-inputs { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }\n    .sk-inputs.is-wide { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; }\n    .sk-card { background: #fff; border: 1px solid var(--sk-border); border-radius: 4px; padding: 14px 15px 15px; min-width: 0; }\n    .sk-box .sk-label { display: block; font-size: 0.92rem; font-weight: 800; color: var(--sk-ink); margin: 0 0 2px; padding: 0; line-height: 1.3; }\n    .sk-box .sk-help { font-size: 0.76rem; color: var(--sk-muted); margin: 0 0 8px; line-height: 1.45; }\n    .sk-box .sk-input {\n        display: block !important;\n        width: 100% !important;\n        max-width: 100% !important;\n        font-family: var(--sk-mono) !important;\n        font-size: 0.9rem !important;\n        line-height: 1.45 !important;\n        color: var(--sk-ink) !important;\n        background: #fff !important;\n        border: 1px solid var(--sk-border) !important;\n        border-radius: 3px !important;\n        padding: 7px 10px !important;\n        min-height: 150px !important;\n        margin: 0 !important;\n        box-shadow: none !important;\n        outline: none !important;\n        height: auto !important;\n        resize: vertical !important;\n        white-space: pre !important;\n        overflow: auto !important;\n        -webkit-appearance: none !important;\n        appearance: none !important;\n    }\n    \/* Platzhalter kursiv, damit das Beispiel nicht wie eine Eingabe aussieht *\/\n    .sk-box .sk-input::placeholder { color: #64748b !important; opacity: 1 !important; font-style: italic !important; }\n    .sk-box .sk-input:focus { border-color: var(--sk-primary) !important; box-shadow: none !important; }\n    .sk-box .sk-input:focus-visible { outline: 2px solid #3f5f75 !important; outline-offset: 1px !important; }\n    .sk-box .sk-input.is-over { border-color: var(--sk-primary) !important; background: #f0f5fa !important; }\n    @media (pointer: coarse) {\n        .sk-box .sk-input, .sk-box .sk-select { font-size: 16px !important; }\n    }\n    .sk-box .sk-status { font-size: 0.8rem; color: var(--sk-muted); margin: 6px 0 0; line-height: 1.5; }\n    .sk-box .sk-status:empty { display: none; }\n    .sk-box .sk-status .sk-status-line { display: block; }\n    .sk-box .sk-example { font-size: 0.8rem; color: var(--sk-muted); margin: 2px 0 0; line-height: 1.5; overflow-wrap: anywhere; word-break: break-word; }\n    .sk-box .sk-example:empty { display: none; }\n    .sk-box .sk-example .sk-mono { font-family: var(--sk-mono); font-weight: 700; color: var(--sk-ink); }\n    .sk-box .sk-notes p { font-size: 0.8rem; color: var(--sk-muted); margin: 4px 0 0; line-height: 1.5; }\n    .sk-box .sk-notes:empty { display: none; }\n    .sk-box .sk-filerow { margin-top: 8px; }\n\n    \/* Statuszeile der Punkte mit Spaltenwahl: nebeneinander, bei Platzmangel untereinander *\/\n    .sk-box .sk-statusrow { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 6px 14px; margin-top: 6px; }\n    .sk-box .sk-statuscol { flex: 1 1 200px; min-width: 0; }\n    .sk-box .sk-statusrow .sk-status { margin: 0; }\n    .sk-box .sk-colpick { flex: 0 0 auto; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }\n    .sk-box .sk-collabel { display: inline; font-size: 0.8rem; font-weight: 700; color: var(--sk-ink); margin: 0; padding: 0; line-height: 1.3; }\n    .sk-box .sk-select {\n        -webkit-appearance: none !important;\n        -moz-appearance: none !important;\n        appearance: none !important;\n        \/* eigener Pfeil aus zwei Verlaeufen, damit Safari die Mindesthoehe einhaelt (Muster Punktdatei-Inspektor) *\/\n        background-image: linear-gradient(45deg, transparent 50%, #3f5f75 50%), linear-gradient(135deg, #3f5f75 50%, transparent 50%) !important;\n        background-position: calc(100% - 15px) 52%, calc(100% - 10px) 52% !important;\n        background-size: 5px 5px, 5px 5px !important;\n        background-repeat: no-repeat !important;\n        background-color: #fff !important;\n        display: inline-block !important;\n        width: auto !important;\n        max-width: 100% !important;\n        font-family: inherit !important;\n        font-size: 0.8rem !important;\n        line-height: 1.3 !important;\n        color: var(--sk-ink) !important;\n        border: 1px solid var(--sk-border) !important;\n        border-radius: 3px !important;\n        min-height: 36px !important;\n        height: auto !important;\n        padding: 6px 28px 6px 10px !important;\n        margin: 0 !important;\n        box-shadow: none !important;\n        outline: none !important;\n        text-transform: none !important;\n        letter-spacing: 0 !important;\n        cursor: pointer !important;\n    }\n    .sk-box .sk-select:hover { background-color: #edf4f8 !important; color: var(--sk-ink) !important; border-color: var(--sk-primary) !important; }\n    .sk-box .sk-select:focus { border-color: var(--sk-primary) !important; box-shadow: none !important; }\n    .sk-box .sk-select:focus-visible { outline: 2px solid var(--sk-primary) !important; outline-offset: 1px !important; }\n\n    \/* Nebenknopf (Datei w\u00e4hlen, Codes kopieren) *\/\n    .sk-box .sk-side {\n        -webkit-appearance: none !important;\n        appearance: none !important;\n        font-family: inherit !important;\n        font-size: 0.74rem !important;\n        font-weight: 700 !important;\n        line-height: 1.2 !important;\n        color: var(--sk-primary-deep) !important;\n        background: #fff !important;\n        border: 1px solid var(--sk-border) !important;\n        border-radius: 3px !important;\n        min-height: 36px !important;\n        padding: 6px 12px !important;\n        margin: 0 !important;\n        cursor: pointer !important;\n        box-shadow: none !important;\n        text-transform: none !important;\n        letter-spacing: 0 !important;\n        text-decoration: none !important;\n        transition: background-color 0.15s, color 0.15s, border-color 0.15s;\n    }\n    .sk-box .sk-side:hover { background: #edf4f8 !important; color: var(--sk-primary-deep) !important; border-color: var(--sk-primary) !important; }\n    .sk-box .sk-side:active { background: #e2ecf2 !important; color: var(--sk-primary-deep) !important; }\n    .sk-box .sk-side:focus-visible { outline: 2px solid var(--sk-primary) !important; outline-offset: 2px !important; }\n    .sk-box .sk-side:disabled,\n    .sk-box .sk-side:disabled:hover { opacity: 0.5 !important; cursor: not-allowed !important; background: #f1f5f9 !important; color: var(--sk-muted) !important; border-color: var(--sk-border) !important; }\n\n    \/* Aufklapper *\/\n    .sk-box summary {\n        cursor: pointer;\n        font-family: inherit;\n        font-weight: 700;\n        color: #2c4a5e !important;\n        background: transparent !important;\n        line-height: 1.4;\n        border-radius: 3px;\n        min-height: 34px;\n        display: flex;\n        align-items: center;\n        gap: 6px;\n        list-style: none;\n    }\n    .sk-box summary::-webkit-details-marker { display: none; }\n    .sk-box summary::before { content: \"\\25B8\"; display: inline-block; flex-shrink: 0; width: 0.9em; transition: transform 0.2s; }\n    .sk-box details[open] > summary::before { transform: rotate(90deg); }\n    .sk-box summary:hover { color: #1e3548 !important; background: #edf4f8 !important; }\n    .sk-box summary:focus-visible { outline: 2px solid var(--sk-primary); outline-offset: 2px; }\n    .sk-box .sk-howto { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--sk-hair); }\n    .sk-box .sk-howto summary { font-size: 0.78rem; padding: 3px 4px; }\n    .sk-box .sk-howto p { font-size: 0.76rem; line-height: 1.55; color: var(--sk-text); margin: 6px 0 0; }\n\n    \/* ---------- Ergebnis ---------- *\/\n    .sk-result { margin-top: 14px; }\n    .sk-box .sk-summary { margin: 0; }\n    .sk-box .sk-summary[data-sk-state=\"leer\"] { font-size: 0.84rem; color: var(--sk-muted); }\n    .sk-box .sk-summary[data-sk-state=\"ok\"],\n    .sk-box .sk-summary[data-sk-state=\"pruefen\"] {\n        background: #fff;\n        border: 1px solid var(--sk-border);\n        border-left: 3px solid var(--sk-primary);\n        border-radius: 0 4px 4px 0;\n        padding: 9px 14px;\n        font-size: 0.88rem;\n        line-height: 1.55;\n        color: var(--sk-ink);\n    }\n    .sk-box .sk-sum-1 { font-weight: 800; color: var(--sk-ink); }\n    .sk-box .sk-sum-2 { display: flex; align-items: flex-start; gap: 8px; color: var(--sk-text); }\n    .sk-sq { display: inline-block; width: 9px; height: 9px; border-radius: 1px; flex-shrink: 0; background: var(--sk-good); margin-top: 0.5em; }\n    .sk-sq.is-warn { background: var(--sk-warn); }\n\n    .sk-sec { margin-top: 16px; }\n    .sk-box .sk-sec-title { font-size: 0.92rem; font-weight: 800; color: var(--sk-ink); line-height: 1.3; margin: 0; }\n    .sk-box .sk-sec-text { font-size: 0.8rem; color: var(--sk-text); line-height: 1.5; margin: 3px 0 0; }\n    .sk-box .sk-btnrow { margin-top: 10px; }\n    .sk-box .sk-keylist { font-size: 0.8rem; line-height: 1.7; margin: 8px 0 0; overflow-wrap: anywhere; word-break: break-word; }\n    .sk-box .sk-keylist .sk-mono { font-family: var(--sk-mono); font-weight: 700; color: var(--sk-ink); }\n\n    .sk-box .sk-all { margin-top: 16px; }\n    .sk-box .sk-all summary { font-size: 0.92rem; font-weight: 800; padding: 4px 6px; margin-left: -6px; }\n\n    .sk-box table.sk-table {\n        width: 100% !important;\n        margin: 8px 0 0 !important;\n        border-collapse: separate !important;\n        border-spacing: 0 !important;\n        border: 1px solid var(--sk-border) !important;\n        border-radius: 4px !important;\n        overflow: hidden !important;\n        background: #fff !important;\n        table-layout: auto !important;\n    }\n    .sk-box .sk-table tr { background: transparent !important; }\n    .sk-box .sk-table th,\n    .sk-box .sk-table td {\n        padding: 6px 10px !important;\n        border: 0 !important;\n        border-bottom: 1px solid var(--sk-hair) !important;\n        background: transparent !important;\n        color: var(--sk-text) !important;\n        font-size: 0.8rem !important;\n        line-height: 1.45 !important;\n        text-align: left !important;\n        vertical-align: top !important;\n        overflow-wrap: break-word;\n    }\n    .sk-box .sk-table th:nth-child(1) { width: 30% !important; }\n    .sk-box .sk-table th:nth-child(2) { width: 1% !important; }\n    .sk-box .sk-table thead th {\n        background: #f0f5fa !important;\n        color: var(--sk-ink) !important;\n        font-size: 0.74rem !important;\n        font-weight: 800 !important;\n        border-bottom: 1px solid var(--sk-border) !important;\n    }\n    .sk-box .sk-table tbody tr:last-child td { border-bottom: 0 !important; }\n    .sk-box .sk-table .sk-num { text-align: right !important; white-space: nowrap; font-variant-numeric: tabular-nums; }\n    .sk-box .sk-table td.sk-mono,\n    .sk-box .sk-table td .sk-mono { font-family: var(--sk-mono) !important; font-weight: 700 !important; color: var(--sk-ink) !important; overflow-wrap: anywhere; word-break: break-word; }\n    .sk-box .sk-table td.sk-none { color: var(--sk-muted) !important; }\n\n    \/* Fu\u00dfzeile *\/\n    .sk-box a.sk-link, .sk-box a.sk-link:visited { color: #2c4a5e !important; background: transparent !important; text-decoration: underline !important; font-weight: 700; }\n    .sk-box a.sk-link:hover, .sk-box a.sk-link:focus { color: #1e3548 !important; background: #edf4f8 !important; text-decoration: underline !important; }\n    .sk-foot { margin-top: 14px; padding-top: 9px; border-top: 1px solid var(--sk-hair); font-size: 0.8rem; line-height: 1.55; color: var(--sk-muted); }\n    .sk-box .sk-foot p { margin: 0 0 6px; }\n    .sk-box .sk-foot p:last-child { margin-bottom: 0; }\n    .sk-box .sk-foot details { margin: 0 0 6px; }\n    .sk-box .sk-foot details summary { font-size: 0.8rem; padding: 3px 4px; margin-left: -4px; }\n    .sk-box .sk-foot details p { margin: 2px 0 0; }\n    .sk-foot strong { color: var(--sk-text); }\n\n    @media (max-width: 700px) {\n        .sk-header { padding: 12px 14px 11px; }\n        .sk-header-top { gap: 10px; }\n        .sk-header-icon { width: 28px; height: 28px; }\n        .sk-body { padding: 10px 8px 12px; }\n        .sk-privacy { font-size: 0.76rem; padding: 5px 10px; }\n        .sk-card { padding: 10px 10px 11px; }\n        .sk-box .sk-box-desc { font-size: 0.8rem; }\n        .sk-foot { font-size: 0.76rem; line-height: 1.5; }\n        .sk-box .sk-foot details summary { font-size: 0.76rem; }\n        .sk-box .sk-status, .sk-box .sk-example, .sk-box .sk-notes p { font-size: 0.76rem; }\n        .sk-box .sk-table th,\n        .sk-box .sk-table td { padding: 6px 8px !important; }\n    }\n    @media (prefers-reduced-motion: reduce) {\n        \/* !important n\u00f6tig: die Barrierefreiheits-Regel muss jede Spezifit\u00e4t der Box schlagen. *\/\n        .sk-box * { transition: none !important; animation: none !important; }\n    }\n<\/style>\n\n<div class=\"sk-box\" data-sk-root lang=\"de\">\n    <div class=\"sk-header\">\n        <div class=\"sk-header-top\">\n            <svg class=\"sk-header-icon\" viewBox=\"0 0 36 36\" fill=\"none\" aria-hidden=\"true\">\n                <circle cx=\"10\" cy=\"10.5\" r=\"5.2\" stroke=\"rgba(255,255,255,0.92)\" stroke-width=\"1.8\"\/>\n                <path d=\"M10 15.7 V32\" stroke=\"rgba(255,255,255,0.92)\" stroke-width=\"1.8\"\/>\n                <path d=\"M10 24 H15.5 M10 29 H14\" stroke=\"rgba(255,255,255,0.92)\" stroke-width=\"1.8\"\/>\n                <rect x=\"21\" y=\"7.5\" width=\"13\" height=\"3\" fill=\"rgba(255,255,255,0.85)\"\/>\n                <rect x=\"21\" y=\"16.5\" width=\"10\" height=\"3\" fill=\"rgba(255,255,255,0.6)\"\/>\n                <rect x=\"21\" y=\"25.5\" width=\"12\" height=\"3\" fill=\"rgba(255,255,255,0.6)\"\/>\n            <\/svg>\n            <div>\n                <h2 class=\"sk-box-title\">Schl\u00fcssel-Check<\/h2>\n                <div class=\"sk-box-desc\">F\u00fcg deinen Beschreibungsschl\u00fcsselsatz und die Kurzbeschreibungen deiner Punkte ein. Du siehst sofort, welche Codes keinen Schl\u00fcssel treffen.<\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div class=\"sk-body\">\n        <div class=\"sk-privacy\">Deine Daten werden nur in deinem Browser gelesen und nirgends hochgeladen.<\/div>\n\n        <div class=\"sk-inputs\" id=\"skInputs\">\n            <div class=\"sk-card\">\n                <label class=\"sk-label\" for=\"skKeys\">Deine Schl\u00fcssel<\/label>\n                <p class=\"sk-help\" id=\"skKeysHelp\">Ein Schl\u00fcssel je Zeile, zum Beispiel BAUM* oder MH. Aus dem Editor kopierte Zeilen gehen auch.<\/p>\n                <textarea class=\"sk-input\" id=\"skKeys\" rows=\"8\" wrap=\"off\" spellcheck=\"false\" autocapitalize=\"off\" autocomplete=\"off\" placeholder=\"BAUM*&#10;GEL*&#10;MH\" aria-describedby=\"skKeysHelp skKeysStatus\"><\/textarea>\n                <p class=\"sk-status\" id=\"skKeysStatus\"><\/p>\n                <div class=\"sk-notes\" id=\"skKeysNotes\"><\/div>\n                <details class=\"sk-howto\">\n                    <summary>So kopierst du die Schl\u00fcssel aus Civil 3D<\/summary>\n                    <p>Im Projektbrowser auf der Registerkarte Einstellungen die Sammlung Punkt aufklappen, dann Beschreibungsschl\u00fcssels\u00e4tze. Mit der rechten Maustaste auf deinen Satz klicken und Schl\u00fcssel bearbeiten w\u00e4hlen; der Editor \u00f6ffnet sich im Panoramafenster. Dort die erste Zeile anklicken, die letzte mit gedr\u00fcckter Umschalttaste, mit der rechten Maustaste klicken und die Zeilen in die Zwischenablage kopieren. Der Eintrag im Kontextmen\u00fc hei\u00dft je nach Version etwas anders. Hier einf\u00fcgen gen\u00fcgt: Das Werkzeug nimmt die Spalte Code, ohne Kopfzeile die erste Spalte.<\/p>\n                <\/details>\n            <\/div>\n\n            <div class=\"sk-card\">\n                <label class=\"sk-label\" for=\"skPoints\">Deine Punkte<\/label>\n                <p class=\"sk-help\" id=\"skPointsHelp\">Zeilen aus der Punktdatei oder nur die Kurzbeschreibungen, eine je Zeile. Eine Datei kannst du auch auf das Feld ziehen.<\/p>\n                <textarea class=\"sk-input\" id=\"skPoints\" rows=\"8\" wrap=\"off\" spellcheck=\"false\" autocapitalize=\"off\" autocomplete=\"off\" placeholder=\"1028,512387.40,5403144.08,313.13,GEL\" aria-describedby=\"skPointsHelp skPointsStatus\"><\/textarea>\n                <div class=\"sk-filerow\">\n                    <button type=\"button\" class=\"sk-side\" id=\"skFile\">Datei w\u00e4hlen<\/button>\n                    <input type=\"file\" id=\"skFileInput\" hidden tabindex=\"-1\">\n                <\/div>\n                <div class=\"sk-statusrow\">\n                    <div class=\"sk-statuscol\">\n                        <p class=\"sk-status\" id=\"skPointsStatus\"><\/p>\n                        <p class=\"sk-example\" id=\"skPointsExample\"><\/p>\n                    <\/div>\n                    <div class=\"sk-colpick\">\n                        <label class=\"sk-collabel\" for=\"skCol\">Kurzbeschreibung<\/label>\n                        <select class=\"sk-select\" id=\"skCol\">\n                            <option value=\"auto\" selected>automatisch<\/option>\n                            <option value=\"zeile\">ganze Zeile<\/option>\n                            <option value=\"2\">ab Spalte 2<\/option>\n                            <option value=\"3\">ab Spalte 3<\/option>\n                            <option value=\"4\">ab Spalte 4<\/option>\n                            <option value=\"5\">ab Spalte 5<\/option>\n                            <option value=\"6\">ab Spalte 6<\/option>\n                        <\/select>\n                    <\/div>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <div class=\"sk-result\" id=\"skResult\">\n            <div class=\"sk-summary\" id=\"skSummary\" aria-live=\"polite\" data-sk-state=\"leer\"><\/div>\n            <div id=\"skSections\"><\/div>\n        <\/div>\n\n        <div class=\"sk-foot\">\n            <p><strong>So pr\u00fcft das Werkzeug:<\/strong> Es vergleicht nur das erste Wort der Kurzbeschreibung, also alles bis zum ersten Leerzeichen, und der Schl\u00fcssel muss es vollst\u00e4ndig treffen. Gro\u00df- und Kleinschreibung z\u00e4hlt.<\/p>\n            <details>\n                <summary>Platzhalter im \u00dcberblick<\/summary>\n                <p># eine Ziffer, @ ein Buchstabe, . ein Zeichen, das weder Ziffer noch Buchstabe ist, * beliebig viele Zeichen, ? genau ein Zeichen, ~ am Anfang alles au\u00dfer dem Muster, [AB] eines der Zeichen, [~AB] keines davon, [A-G] ein Bereich. Ein Backtick (`) vor einem Platzhalter macht ihn hier zum normalen Zeichen; ob Civil 3D genau dieses Zeichen daf\u00fcr nimmt, ist nicht belegt.<\/p>\n            <\/details>\n            <p><strong>Nicht nachgebildet:<\/strong> die Reihenfolge innerhalb eines Schl\u00fcsselsatzes und zwischen mehreren S\u00e4tzen und die Einstellung, ob auch auf die Parameter $1 und $2 abgeglichen wird. Ein Treffer hei\u00dft auch nicht, dass ein vorhandener Punkt schon so aussieht: Auf Punkte, die vor dem Schl\u00fcssel da waren, wirkt er erst nach dem Befehl Beschreibungsschl\u00fcssel anwenden.<\/p>\n            <p>Hintergrund im Tutorial <a class=\"sk-link\" href=\"https:\/\/cad-kompass.de\/?p=2795#schluessel\">Punktgruppen, Beschreibungsschl\u00fcssel und Darstellung<\/a>.<\/p>\n        <\/div>\n    <\/div>\n<\/div>\n\n<script>\n(function () {\n    \"use strict\";\n    var ROOT = document.querySelector(\"[data-sk-root]\");\n    if (!ROOT) { return; }\n\n    \/\/ ============================================================\n    \/\/ REIN (ohne DOM): Abgleich, Einlesen, Auswertung\n    \/\/ ============================================================\n    var MAX_BYTES = 5 * 1024 * 1024;\n    var MAX_LINES = 100000;\n    var NUMRE = \/^[+-]?[0-9]+([.,][0-9]+)?$\/;\n    var BOOLWORDS = [\"true\", \"false\", \"wahr\", \"falsch\", \"ja\", \"nein\"];\n\n    function isDigit(ch) { return ch >= \"0\" ? ch <= \"9\" : false; }\n    function isLetter(ch) { return ch.toLowerCase() !== ch.toUpperCase(); }\n    function isLower(ch) { return ch !== ch.toUpperCase(); }\n\n    \/\/ ---- Muster in Tokens zerlegen (kein RegExp aus dem Muster) ----\n    var COMPILED = new Map();\n    function compile(code) {\n        var hit = COMPILED.get(code);\n        if (hit) { return hit; }\n        var cs = Array.from(code);\n        var toks = [];\n        var neg = false;\n        var unclosed = false;\n        var i = 0;\n        var c;\n        var j;\n        var close;\n        if (cs.length > 0) {\n            if (cs[0] === \"~\") { neg = true; i = 1; }\n        }\n        while (i < cs.length) {\n            c = cs[i];\n            if (c === \"`\") {\n                if (i + 1 < cs.length) {\n                    toks.push({ k: \"lit\", c: cs[i + 1] });\n                    i += 2;\n                } else {\n                    toks.push({ k: \"lit\", c: \"`\" });\n                    i += 1;\n                }\n            } else if (c === \"[\") {\n                \/\/ Ein ] direkt nach [ bzw. nach [~ geh\u00f6rt zur Menge, erst das n\u00e4chste ] schlie\u00dft.\n                close = -1;\n                j = i + 1;\n                if (j < cs.length) {\n                    if (cs[j] === \"~\") { j += 1; }\n                }\n                if (j < cs.length) {\n                    if (cs[j] === \"]\") { j += 1; }\n                }\n                for (; j < cs.length; j += 1) {\n                    if (cs[j] === \"]\") { close = j; break; }\n                }\n                if (close < 0) {\n                    unclosed = true;\n                    toks.push({ k: \"lit\", c: \"[\" });\n                    i += 1;\n                } else {\n                    var inner = cs.slice(i + 1, close);\n                    var setNeg = false;\n                    var p = 0;\n                    var items = [];\n                    if (inner.length > 0) {\n                        if (inner[0] === \"~\") { setNeg = true; p = 1; }\n                    }\n                    while (p < inner.length) {\n                        if (p + 2 < inner.length) {\n                            if (inner[p + 1] === \"-\") {\n                                items.push({ lo: inner[p].codePointAt(0), hi: inner[p + 2].codePointAt(0) });\n                                p += 3;\n                                continue;\n                            }\n                        }\n                        items.push({ lo: inner[p].codePointAt(0), hi: inner[p].codePointAt(0) });\n                        p += 1;\n                    }\n                    toks.push({ k: \"set\", neg: setNeg, items: items });\n                    i = close + 1;\n                }\n            } else if (c === \"#\") {\n                toks.push({ k: \"digit\" });\n                i += 1;\n            } else if (c === \"@\") {\n                toks.push({ k: \"alpha\" });\n                i += 1;\n            } else if (c === \".\") {\n                toks.push({ k: \"punct\" });\n                i += 1;\n            } else if (c === \"*\") {\n                toks.push({ k: \"star\" });\n                i += 1;\n            } else if (c === \"?\") {\n                toks.push({ k: \"any\" });\n                i += 1;\n            } else {\n                toks.push({ k: \"lit\", c: c });\n                i += 1;\n            }\n        }\n        var out = { neg: neg, toks: toks, unclosed: unclosed };\n        COMPILED.set(code, out);\n        return out;\n    }\n\n    function single(t, ch) {\n        var cp;\n        var found;\n        var n;\n        if (t.k === \"lit\") { return ch === t.c; }\n        if (t.k === \"digit\") { return isDigit(ch); }\n        if (t.k === \"alpha\") { return isLetter(ch); }\n        if (t.k === \"punct\") { return isDigit(ch) ? false : !isLetter(ch); }\n        if (t.k === \"any\") { return true; }\n        \/\/ Menge\n        cp = ch.codePointAt(0);\n        found = false;\n        for (n = 0; n < t.items.length; n += 1) {\n            if (cp >= t.items[n].lo) {\n                if (cp <= t.items[n].hi) { found = true; break; }\n            }\n        }\n        return t.neg ? !found : found;\n    }\n\n    \/\/ Dynamische Programmierung von hinten: next[j] hei\u00dft \"ab Token i+1 passt der Rest ab Zeichen j\".\n    function runTokens(toks, es) {\n        var n = es.length;\n        var next = new Array(n + 1);\n        var cur;\n        var i;\n        var j;\n        var t;\n        for (j = 0; j <= n; j += 1) { next[j] = j === n; }\n        for (i = toks.length - 1; i >= 0; i -= 1) {\n            t = toks[i];\n            cur = new Array(n + 1);\n            if (t.k === \"star\") {\n                cur[n] = next[n];\n                for (j = n - 1; j >= 0; j -= 1) { cur[j] = next[j] || cur[j + 1]; }\n            } else {\n                cur[n] = false;\n                for (j = n - 1; j >= 0; j -= 1) {\n                    cur[j] = next[j + 1] ? single(t, es[j]) : false;\n                }\n            }\n            next = cur;\n        }\n        return next[0];\n    }\n\n    function match(code, element) {\n        var p = compile(String(code));\n        var res = runTokens(p.toks, Array.from(String(element)));\n        return p.neg ? !res : res;\n    }\n\n    \/\/ Getrennt wird nur am normalen Leerzeichen (U+0020), nicht an Tab oder gesch\u00fctztem Leerzeichen.\n    function firstElement(text) {\n        var t = String(text).trim();\n        var k = t.indexOf(\" \");\n        return k < 0 ? t : t.slice(0, k);\n    }\n\n    \/\/ ---- Schl\u00fcssel einlesen ----\n    \/\/ Tab-Zeile: mit Kopfzeile (Zelle \"Code\") gilt deren Spalte, sonst die erste Zelle, die kein Wahrheitswort ist.\n    function keyCodeOfLine(raw, codeCol) {\n        var line = raw.trim();\n        var cells;\n        var cell = line;\n        var i;\n        if (raw.indexOf(\"\\t\") < 0) { return firstElement(line); }\n        cells = raw.split(\"\\t\");\n        if (codeCol >= 0) {\n            return codeCol < cells.length ? firstElement(cells[codeCol]) : \"\";\n        }\n        for (i = 0; i < cells.length; i += 1) {\n            var c = cells[i].trim();\n            if (c !== \"\") {\n                if (BOOLWORDS.indexOf(c.toLowerCase()) < 0) { cell = c; break; }\n            }\n        }\n        return firstElement(cell);\n    }\n\n    function codeColumnOf(raw) {\n        var cells = raw.split(\"\\t\");\n        var i;\n        for (i = 0; i < cells.length; i += 1) {\n            if (cells[i].trim().toLowerCase() === \"code\") { return i; }\n        }\n        return -1;\n    }\n\n    function parseKeys(text) {\n        var lines = String(text).split(\/\\r\\n|\\r|\\n\/);\n        var codes = [];\n        var notes = [];\n        var seen = new Set();\n        var dupDone = new Set();\n        var codeCol = -1;\n        var i;\n        for (i = 0; i < lines.length; i += 1) {\n            var raw = lines[i];\n            if (raw.trim() === \"\") { continue; }\n            if (codeCol < 0) {\n                if (raw.indexOf(\"\\t\") >= 0) {\n                    codeCol = codeColumnOf(raw);\n                    if (codeCol >= 0) { continue; }\n                }\n            }\n            var code = keyCodeOfLine(raw, codeCol);\n            if (code === \"\") { continue; }\n            if (code.toLowerCase() === \"code\") { continue; }\n            if (seen.has(code)) {\n                if (!dupDone.has(code)) {\n                    dupDone.add(code);\n                    notes.push(code + \" steht mehrfach in der Liste und z\u00e4hlt einmal.\");\n                }\n                continue;\n            }\n            seen.add(code);\n            codes.push(code);\n            if (compile(code).unclosed) {\n                notes.push(code + \": eckige Klammer ohne Gegenst\u00fcck, als Zeichen gelesen.\");\n            }\n            if (code.indexOf(\",\") >= 0) {\n                notes.push(code + \" enth\u00e4lt ein Komma. In AutoCAD trennt ein Komma Muster; ob Civil 3D das bei Beschreibungsschl\u00fcsseln auch tut, ist nicht dokumentiert. Hier z\u00e4hlt das Komma als Zeichen.\");\n            }\n        }\n        return { codes: codes, notes: notes };\n    }\n\n    \/\/ ---- Punkte einlesen ----\n    function unquote(x) {\n        if (x.length >= 2) {\n            if (x.charAt(0) === '\"') {\n                if (x.charAt(x.length - 1) === '\"') { return x.slice(1, x.length - 1); }\n            }\n        }\n        return x;\n    }\n\n    \/\/ sep: Trennzeichen oder null f\u00fcr Leerraum\n    function splitBy(line, sep) {\n        var f = sep === null ? line.split(\/\\s+\/) : line.split(sep);\n        return f.map(function (x) { return unquote(x.trim()); });\n    }\n    \/\/ R\u00fcckfall ohne erkannte Punktdatei-Zeile: das erste vorhandene Trennzeichen (Tab, Semikolon, Komma, sonst Leerraum)\n    function splitFields(line) {\n        if (line.indexOf(\"\\t\") >= 0) { return splitBy(line, \"\\t\"); }\n        if (line.indexOf(\";\") >= 0) { return splitBy(line, \";\"); }\n        if (line.indexOf(\",\") >= 0) { return splitBy(line, \",\"); }\n        return splitBy(line, null);\n    }\n\n    \/\/ Koordinaten\u00e4hnlich: Dezimaltrennzeichen oder mindestens vier Ziffern vor dem Komma (ohne Vorzeichen).\n    function coordLike(x) {\n        if (x.search(\/[.,]\/) >= 0) { return true; }\n        return x.replace(\/^[+-]\/, \"\").length >= 4;\n    }\n    function coordCount(fields, from, to) {\n        var c = 0;\n        var q;\n        for (q = from; q < to; q += 1) {\n            if (coordLike(fields[q])) { c += 1; }\n        }\n        return c;\n    }\n\n    \/\/ Eine Zerlegung einordnen: \"front\" = Punktname vorne (Feld 1 keine Zahl, Felder 2 bis 4 Zahlen),\n    \/\/ \"n4\" = mindestens vier f\u00fchrende Zahlen, \"n3\" = genau drei, sonst \"none\". Der betrachtete Zahlenblock\n    \/\/ muss mindestens zwei koordinaten\u00e4hnliche Zahlen enthalten, sonst gilt die Zeile als reine Kurzbeschreibung.\n    function classify(fields) {\n        var n = 0;\n        var res = { kind: \"none\", start: 0, score: 0 };\n        while (n < fields.length) {\n            if (NUMRE.test(fields[n])) { n += 1; } else { break; }\n        }\n        if (n === 0) {\n            if (fields.length >= 4) {\n                if (NUMRE.test(fields[1])) {\n                    if (NUMRE.test(fields[2])) {\n                        if (NUMRE.test(fields[3])) {\n                            if (coordCount(fields, 1, 4) >= 2) { res = { kind: \"front\", start: 4, score: 3 }; }\n                        }\n                    }\n                }\n            }\n        } else if (n >= 3) {\n            if (coordCount(fields, 0, n) >= 2) { res = { kind: n >= 4 ? \"n4\" : \"n3\", start: n >= 4 ? 4 : 3, score: n }; }\n        }\n        return res;\n    }\n\n    \/\/ Eine Zeile zerlegen und einordnen. Das Trennzeichen w\u00e4hlt je Zeile die Zerlegung mit dem gr\u00f6\u00dften Zahlenblock,\n    \/\/ der die Bedingung erf\u00fcllt (bei Gleichstand gewinnt die Reihenfolge Tab, Semikolon, Komma, Leerraum).\n    \/\/ Erf\u00fcllt keine, bleibt kind \"none\" und die Zerlegung ist der R\u00fcckfall splitFields.\n    function analyzeLine(line) {\n        var seps = [\"\\t\", \";\", \",\", null];\n        var best = null;\n        var bestFields = null;\n        var s;\n        for (s = 0; s < seps.length; s += 1) {\n            if (seps[s] !== null) {\n                if (line.indexOf(seps[s]) < 0) { continue; }\n            }\n            var f = splitBy(line, seps[s]);\n            var c = classify(f);\n            if (c.kind !== \"none\") {\n                if (best === null) { best = c; bestFields = f; }\n                else if (c.score > best.score) { best = c; bestFields = f; }\n            }\n        }\n        if (best !== null) {\n            return { fields: bestFields, kind: best.kind, start: best.start, anyNum: true };\n        }\n        var fb = splitFields(line);\n        return { fields: fb, kind: \"none\", start: 0, anyNum: fb.some(function (x) { return NUMRE.test(x); }) };\n    }\n\n    \/\/ colMode: \"auto\" (oder weggelassen) | \"zeile\" | 2 bis 6 (Kurzbeschreibung ab dieser Spalte)\n    function parsePoints(text, colMode) {\n        var lines = String(text).split(\/\\r\\n|\\r|\\n\/);\n        var sel = \"auto\";\n        var cn = Number(colMode);\n        if (colMode === \"zeile\") { sel = \"zeile\"; }\n        else if (cn >= 2) { if (cn <= 6) { sel = Math.floor(cn); } }\n        var items = [];\n        var elements = [];\n        var shorts = [];\n        var capped = false;\n        var skipped = 0;\n        var exampleLine = 0;\n        var i;\n        for (i = 0; i < lines.length; i += 1) {\n            var line = lines[i].trim();\n            if (line === \"\") { continue; }\n            if (line.charAt(0) === \"#\") { skipped += 1; continue; }\n            if (items.length >= MAX_LINES) { capped = true; break; }\n            items.push({ no: i + 1, line: line, a: analyzeLine(line) });\n        }\n        \/\/ F\u00fchrende Zeilen ohne Zahlenfeld sind Kopfzeilen, wenn danach eine Punktdatei-Zeile folgt.\n        if (sel !== \"zeile\") {\n            var lead = 0;\n            while (lead < items.length) {\n                if (items[lead].a.anyNum) { break; }\n                lead += 1;\n            }\n            if (lead > 0) {\n                if (lead < items.length) {\n                    if (items[lead].a.kind !== \"none\") {\n                        skipped += lead;\n                        items = items.slice(lead);\n                    }\n                }\n            }\n        }\n        var z = items.length;\n        var c3 = 0;\n        var c4 = 0;\n        var cf = 0;\n        items.forEach(function (it) {\n            var a = it.a;\n            var desc;\n            if (a.kind === \"n3\") { c3 += 1; }\n            else if (a.kind === \"n4\") { c4 += 1; }\n            else if (a.kind === \"front\") { cf += 1; }\n            if (sel === \"zeile\") { desc = it.line; }\n            else if (sel === \"auto\") { desc = a.kind === \"none\" ? it.line : a.fields.slice(a.start).join(\" \").trim(); }\n            else { desc = a.fields.slice(sel - 1).join(\" \").trim(); }\n            if (desc !== \"\") {\n                var el = firstElement(desc);\n                if (el !== \"\") {\n                    if (exampleLine === 0) { exampleLine = it.no; }\n                    shorts.push(desc);\n                    elements.push(el);\n                }\n            }\n        });\n        var withNum = c3 + c4 + cf;\n        var fileLike = z > 0 ? withNum * 2 >= z : false;\n        var bestStart = 0;\n        if (withNum > 0) { bestStart = c3 >= c4 + cf ? 3 : 4; }\n        return {\n            lines: z,\n            count: elements.length,\n            noDesc: z - elements.length,\n            fileLike: fileLike,\n            col: bestStart + 1,\n            front: bestStart === 4 ? cf > c4 : false,\n            capped: capped,\n            elements: elements,\n            shorts: shorts,\n            skipped: skipped,\n            exampleLine: exampleLine,\n            mode: sel\n        };\n    }\n\n    \/\/ ---- Auswertung ----\n    function firstMatching(codes, s) {\n        var i;\n        for (i = 0; i < codes.length; i += 1) {\n            if (match(codes[i], s)) { return codes[i]; }\n        }\n        return null;\n    }\n\n    function whyNot(e, codes) {\n        var cs = Array.from(e);\n        var up = e.toUpperCase();\n        var s;\n        var i;\n        var hasLower = cs.some(isLower);\n        if (hasLower) {\n            s = firstMatching(codes, up);\n            if (s !== null) {\n                return { kind: \"klein\", text: \"Kleingeschrieben? Als \" + up + \" tr\u00e4fe es \" + s + \". Gro\u00df- und Kleinschreibung z\u00e4hlt.\" };\n            }\n        }\n        var idx = -1;\n        for (i = 0; i < cs.length; i += 1) {\n            if (!isDigit(cs[i])) {\n                if (!isLetter(cs[i])) { idx = i; break; }\n            }\n        }\n        if (idx > 0) {\n            var part = cs.slice(0, idx).join(\"\");\n            s = firstMatching(codes, part);\n            if (s !== null) {\n                return { kind: \"trenner\", text: \"Trennzeichen im Code? \" + part + \" tr\u00e4fe \" + s + \". Den Code von den Parametern trennt nur ein Leerzeichen.\" };\n            }\n        }\n        var L = 0;\n        while (L < cs.length) {\n            if (isLetter(cs[L])) { L += 1; } else { break; }\n        }\n        var D = L;\n        while (D < cs.length) {\n            if (isDigit(cs[D])) { D += 1; } else { break; }\n        }\n        if (L > 0) {\n            if (D > L) {\n                if (D === cs.length) {\n                    var letters = cs.slice(0, L).join(\"\");\n                    var digits = cs.slice(L).join(\"\");\n                    s = firstMatching(codes, letters);\n                    if (s !== null) {\n                        return { kind: \"ziffer\", text: \"Ziffern am Code? \" + letters + \" tr\u00e4fe \" + s + \", \" + e + \" nicht. Sind die Ziffern ein Parameter, geh\u00f6rt ein Leerzeichen davor (\" + letters + \" \" + digits + \"); sonst braucht \" + e + \" einen eigenen Schl\u00fcssel.\" };\n                    }\n                }\n            }\n        }\n        return { kind: \"keiner\", text: \"Kein Schl\u00fcssel passt.\" };\n    }\n\n    function cmpRows(a, b) {\n        if (a.count !== b.count) { return b.count - a.count; }\n        if (a.code < b.code) { return -1; }\n        if (a.code > b.code) { return 1; }\n        return 0;\n    }\n\n    function evaluate(codes, elements) {\n        var counts = new Map();\n        elements.forEach(function (e) { counts.set(e, (counts.get(e) || 0) + 1); });\n        var used = new Set();\n        var rows = [];\n        counts.forEach(function (n, e) {\n            var ks = codes.filter(function (k) { return match(k, e); });\n            ks.forEach(function (k) { used.add(k); });\n            rows.push({ code: e, count: n, keys: ks, reason: ks.length === 0 ? whyNot(e, codes) : null });\n        });\n        rows.sort(cmpRows);\n        return {\n            points: elements.length,\n            rows: rows,\n            ohne: rows.filter(function (r) { return r.keys.length === 0; }),\n            mehrere: rows.filter(function (r) { return r.keys.length > 1; }),\n            unbenutzt: codes.filter(function (k) { return !used.has(k); })\n        };\n    }\n\n    \/\/ ---- Zahlen und Z\u00e4hlformen ----\n    function fmt(n) {\n        var s = String(n);\n        var out = \"\";\n        var i;\n        for (i = 0; i < s.length; i += 1) {\n            if (i > 0) {\n                if ((s.length - i) % 3 === 0) { out += \".\"; }\n            }\n            out += s.charAt(i);\n        }\n        return out;\n    }\n    function cnt(n, one, many) { return n === 1 ? \"1 \" + one : fmt(n) + \" \" + many; }\n\n    \/\/ ============================================================\n    \/\/ DOM\n    \/\/ ============================================================\n    function $(id) { return ROOT.querySelector(\"#\" + id); }\n    function h(tag, cls, txt) {\n        var n = document.createElement(tag);\n        if (cls) { n.className = cls; }\n        if (typeof txt === \"string\") { n.textContent = txt; }\n        return n;\n    }\n    function clear(n) { while (n.firstChild) { n.removeChild(n.firstChild); } }\n\n    var KEYS = $(\"skKeys\");\n    var PTS = $(\"skPoints\");\n    var KSTATUS = $(\"skKeysStatus\");\n    var PSTATUS = $(\"skPointsStatus\");\n    var PEXAMPLE = $(\"skPointsExample\");\n    var COLSEL = $(\"skCol\");\n    var KNOTES = $(\"skKeysNotes\");\n    var SUMMARY = $(\"skSummary\");\n    var SECTIONS = $(\"skSections\");\n    var FILEBTN = $(\"skFile\");\n    var FILEIN = $(\"skFileInput\");\n    var INPUTS = $(\"skInputs\");\n\n    var COPY_LABEL = \"Codes ohne Schl\u00fcssel kopieren\";\n\n    \/\/ ---- Zwischenablage mit R\u00fcckfall ----\n    function fallbackCopy(t) {\n        var ta = document.createElement(\"textarea\");\n        ta.value = t;\n        ta.setAttribute(\"readonly\", \"\");\n        ta.style.position = \"fixed\";\n        ta.style.top = \"-1000px\";\n        ta.style.opacity = \"0\";\n        ta.style.fontSize = \"16px\";\n        document.body.appendChild(ta);\n        ta.select();\n        try { ta.setSelectionRange(0, t.length); } catch (e0) { ta.select(); }\n        var ok = false;\n        try { ok = document.execCommand(\"copy\"); } catch (e) { ok = false; }\n        document.body.removeChild(ta);\n        return ok;\n    }\n    function copyToClipboard(t, done) {\n        var cb = navigator.clipboard;\n        if (cb ? typeof cb.writeText === \"function\" : false) {\n            try {\n                cb.writeText(t).then(function () { done(true); }, function () { done(fallbackCopy(t)); });\n            } catch (e) {\n                done(fallbackCopy(t));\n            }\n        } else {\n            done(fallbackCopy(t));\n        }\n    }\n\n    \/\/ ---- Statuszeilen ----\n    function renderKeyStatus(raw, ki) {\n        clear(KNOTES);\n        if (ki.codes.length === 0) {\n            KSTATUS.textContent = raw.trim() === \"\" ? \"\" : \"Keine Codes gefunden.\";\n        } else {\n            KSTATUS.textContent = cnt(ki.codes.length, \"Schl\u00fcssel\", \"Schl\u00fcssel\") + \" gelesen.\";\n        }\n        ki.notes.forEach(function (t) { KNOTES.appendChild(h(\"p\", \"\", t)); });\n    }\n    function renderPointStatus(raw, pi) {\n        clear(PSTATUS);\n        clear(PEXAMPLE);\n        if (raw.trim() === \"\") { return; }\n        var t = cnt(pi.count, \"Kurzbeschreibung\", \"Kurzbeschreibungen\") + \" aus \" + cnt(pi.lines, \"Zeile\", \"Zeilen\") + \" gelesen.\";\n        if (pi.mode === \"auto\") {\n            if (pi.fileLike) {\n                t += pi.front ? \" Punktdatei erkannt (Punktname vorne), Kurzbeschreibung ab Spalte \" + pi.col + \".\" : \" Punktdatei erkannt, Kurzbeschreibung ab Spalte \" + pi.col + \".\";\n            }\n        }\n        if (pi.skipped > 0) { t += \" \" + cnt(pi.skipped, \"Kopf- oder Kommentarzeile\", \"Kopf- oder Kommentarzeilen\") + \" \u00fcbersprungen.\"; }\n        if (pi.noDesc > 0) { t += \" \" + cnt(pi.noDesc, \"Zeile\", \"Zeilen\") + \" ohne Kurzbeschreibung.\"; }\n        if (pi.exampleLine > 0) {\n            PEXAMPLE.appendChild(document.createTextNode(\"Beispiel aus Zeile \" + String(pi.exampleLine) + \": \"));\n            PEXAMPLE.appendChild(h(\"span\", \"sk-mono\", pi.shorts[0]));\n        }\n        PSTATUS.appendChild(document.createTextNode(t));\n        if (pi.capped) {\n            PSTATUS.appendChild(document.createTextNode(\" \"));\n            PSTATUS.appendChild(h(\"span\", \"sk-status-line\", \"Ausgewertet sind die ersten 100.000 Zeilen.\"));\n        }\n    }\n\n    \/\/ ---- Tabellen ----\n    function buildTable(labelId, heads) {\n        var t = h(\"table\", \"sk-table\");\n        t.setAttribute(\"aria-labelledby\", labelId);\n        var thead = h(\"thead\", \"\");\n        var tr = h(\"tr\", \"\");\n        heads.forEach(function (txt, i) {\n            var th = h(\"th\", i === 1 ? \"sk-num\" : \"\", txt);\n            th.setAttribute(\"scope\", \"col\");\n            tr.appendChild(th);\n        });\n        thead.appendChild(tr);\n        t.appendChild(thead);\n        var body = h(\"tbody\", \"\");\n        t.appendChild(body);\n        return { table: t, body: body };\n    }\n    function addRow(body, r, thirdCls, thirdText, withReason) {\n        var tr = h(\"tr\", \"\");\n        tr.setAttribute(\"data-sk-code\", r.code);\n        tr.setAttribute(\"data-sk-count\", String(r.count));\n        tr.setAttribute(\"data-sk-keys\", r.keys.join(\"|\"));\n        if (withReason) { tr.setAttribute(\"data-sk-reason\", r.reason.kind); }\n        tr.appendChild(h(\"td\", \"sk-mono\", r.code));\n        tr.appendChild(h(\"td\", \"sk-num\", fmt(r.count)));\n        tr.appendChild(h(\"td\", thirdCls, thirdText));\n        body.appendChild(tr);\n    }\n    function section(name, id, title, text) {\n        var s = h(\"section\", \"sk-sec\");\n        s.setAttribute(\"data-sk-section\", name);\n        var hd = h(\"h3\", \"sk-sec-title\", title);\n        hd.id = id;\n        s.appendChild(hd);\n        s.appendChild(h(\"p\", \"sk-sec-text\", text));\n        return s;\n    }\n\n    var copyTimer = null;\n    function onCopy(btn, text) {\n        copyToClipboard(text, function (ok) {\n            btn.textContent = ok ? \"Kopiert.\" : \"Kopieren nicht m\u00f6glich\";\n            if (copyTimer !== null) { window.clearTimeout(copyTimer); }\n            copyTimer = window.setTimeout(function () {\n                copyTimer = null;\n                btn.textContent = COPY_LABEL;\n            }, 2000);\n        });\n    }\n\n    function renderSummary(state, ev, msg) {\n        clear(SUMMARY);\n        SUMMARY.setAttribute(\"data-sk-state\", state);\n        if (state === \"leer\") {\n            SUMMARY.textContent = msg;\n            return;\n        }\n        var C = ev.rows.length;\n        var B = ev.ohne.length;\n        var M = ev.mehrere.length;\n        var l1 = cnt(ev.points, \"Punkt\", \"Punkte\") + \", \" + (C === 1 ? \"1 verschiedener Code\" : fmt(C) + \" verschiedene Codes\") + \".\";\n        SUMMARY.appendChild(h(\"div\", \"sk-sum-1\", l1));\n        var l2 = h(\"div\", \"sk-sum-2\");\n        var sq = h(\"span\", state === \"ok\" ? \"sk-sq\" : \"sk-sq is-warn\");\n        sq.setAttribute(\"aria-hidden\", \"true\");\n        l2.appendChild(sq);\n        if (state === \"ok\") {\n            l2.appendChild(h(\"span\", \"\", \"Jeder Code trifft genau einen Schl\u00fcssel.\"));\n        } else {\n            var parts = [];\n            if (B > 0) { parts.push(B === 1 ? \"1 Code ohne Schl\u00fcssel\" : fmt(B) + \" Codes ohne Schl\u00fcssel\"); }\n            if (M > 0) { parts.push(M === 1 ? \"1 Code mit mehreren Schl\u00fcsseln\" : fmt(M) + \" Codes mit mehreren Schl\u00fcsseln\"); }\n            l2.appendChild(h(\"span\", \"\", parts.join(\", \") + \".\"));\n        }\n        SUMMARY.appendChild(l2);\n    }\n\n    function renderResult(ki, pi) {\n        clear(SECTIONS);\n        var haveKeys = ki.codes.length > 0;\n        var havePts = pi.elements.length > 0;\n        if (!haveKeys) {\n            if (!havePts) { renderSummary(\"leer\", null, \"Das Ergebnis erscheint, sobald beide Felder gef\u00fcllt sind.\"); return; }\n            renderSummary(\"leer\", null, \"F\u00fcg noch deine Schl\u00fcssel ein.\");\n            return;\n        }\n        if (!havePts) { renderSummary(\"leer\", null, \"F\u00fcg noch die Punkte ein.\"); return; }\n\n        var ev = evaluate(ki.codes, pi.elements);\n        var okAll = ev.ohne.length === 0 ? ev.mehrere.length === 0 : false;\n        renderSummary(okAll ? \"ok\" : \"pruefen\", ev, \"\");\n\n        var sec;\n        var tb;\n        if (ev.ohne.length > 0) {\n            sec = section(\"ohne\", \"skH-ohne\", \"Ohne Schl\u00fcssel\", \"Diesen Punkten gibt kein Schl\u00fcssel einen eigenen Stil. Hat niemand von Hand einen Stil gesetzt, sehen sie so aus, wie ihre oberste Punktgruppe es vorgibt.\");\n            tb = buildTable(\"skH-ohne\", [\"Code\", \"Punkte\", \"M\u00f6glicher Grund\"]);\n            ev.ohne.forEach(function (r) { addRow(tb.body, r, \"\", r.reason.text, true); });\n            sec.appendChild(tb.table);\n            var row = h(\"div\", \"sk-btnrow\");\n            var btn = h(\"button\", \"sk-side\", COPY_LABEL);\n            btn.type = \"button\";\n            btn.id = \"skCopy\";\n            var lines = ev.ohne.map(function (r) { return r.code; });\n            btn.addEventListener(\"click\", function () { onCopy(btn, lines.join(\"\\n\")); });\n            row.appendChild(btn);\n            sec.appendChild(row);\n            SECTIONS.appendChild(sec);\n        }\n        if (ev.mehrere.length > 0) {\n            sec = section(\"mehrere\", \"skH-mehrere\", \"Mehrere Schl\u00fcssel passen\", \"Welcher Schl\u00fcssel gewinnt, entscheidet die Reihenfolge im Schl\u00fcsselsatz, und die ist nicht dokumentiert. Pr\u00fcf es an einem Punkt in Civil 3D oder mach die Codes eindeutig.\");\n            tb = buildTable(\"skH-mehrere\", [\"Code\", \"Punkte\", \"Passende Schl\u00fcssel\"]);\n            ev.mehrere.forEach(function (r) { addRow(tb.body, r, \"sk-mono\", r.keys.join(\", \"), false); });\n            sec.appendChild(tb.table);\n            SECTIONS.appendChild(sec);\n        }\n        if (ev.unbenutzt.length > 0) {\n            sec = section(\"unbenutzt\", \"skH-unbenutzt\", \"Schl\u00fcssel ohne Treffer\", \"Diese Schl\u00fcssel trifft kein Code aus deinen Punkten. Das ist in Ordnung, wenn diese Codes in der Lieferung nicht vorkommen.\");\n            var list = h(\"p\", \"sk-keylist\");\n            ev.unbenutzt.forEach(function (k, i) {\n                if (i > 0) { list.appendChild(document.createTextNode(\" \u00b7 \")); }\n                var sp = h(\"span\", \"sk-mono\", k);\n                sp.setAttribute(\"data-sk-key\", k);\n                list.appendChild(sp);\n            });\n            sec.appendChild(list);\n            SECTIONS.appendChild(sec);\n        }\n        var det = h(\"details\", \"sk-all\");\n        det.setAttribute(\"data-sk-section\", \"alle\");\n        var sm = h(\"summary\", \"\", \"Alle Codes (\" + fmt(ev.rows.length) + \")\");\n        sm.id = \"skH-alle\";\n        det.appendChild(sm);\n        tb = buildTable(\"skH-alle\", [\"Code\", \"Punkte\", \"Schl\u00fcssel\"]);\n        ev.rows.forEach(function (r) {\n            if (r.keys.length === 0) { addRow(tb.body, r, \"sk-none\", \"keiner\", false); }\n            else { addRow(tb.body, r, \"sk-mono\", r.keys.join(\", \"), false); }\n        });\n        det.appendChild(tb.table);\n        SECTIONS.appendChild(det);\n    }\n\n    \/\/ ---- Auswertung ausl\u00f6sen (250 ms entprellt) ----\n    var timer = null;\n    function update() {\n        if (timer !== null) { window.clearTimeout(timer); timer = null; }\n        var kraw = KEYS.value;\n        var praw = PTS.value;\n        var ki = parseKeys(kraw);\n        var pi = parsePoints(praw, COLSEL.value);\n        renderKeyStatus(kraw, ki);\n        renderPointStatus(praw, pi);\n        renderResult(ki, pi);\n    }\n    function schedule() {\n        if (timer !== null) { window.clearTimeout(timer); }\n        timer = window.setTimeout(function () { timer = null; update(); }, 250);\n    }\n    KEYS.addEventListener(\"input\", schedule);\n    PTS.addEventListener(\"input\", schedule);\n    COLSEL.addEventListener(\"change\", function () { update(); });\n\n    \/\/ ---- Datei einlesen ----\n    function decodeBytes(buf) {\n        var b = new Uint8Array(buf);\n        var off = 0;\n        if (b.length >= 3) {\n            if (b[0] === 0xEF) {\n                if (b[1] === 0xBB) {\n                    if (b[2] === 0xBF) { off = 3; }\n                }\n            }\n        }\n        var view = off > 0 ? b.subarray(off) : b;\n        if (typeof TextDecoder === \"function\") {\n            var res = null;\n            try { res = new TextDecoder(\"utf-8\", { fatal: true }).decode(view); } catch (e1) { res = null; }\n            if (res !== null) { return res; }\n            try { res = new TextDecoder(\"windows-1252\").decode(view); } catch (e2) { res = null; }\n            if (res !== null) { return res; }\n        }\n        var s = \"\";\n        var i;\n        for (i = 0; i < view.length; i += 1) { s += String.fromCharCode(view[i]); }\n        return s;\n    }\n    var readSeq = 0;\n    function readFile(file) {\n        if (file.size > MAX_BYTES) {\n            PSTATUS.textContent = \"Die Datei ist gr\u00f6\u00dfer als 5 MB. F\u00fcg einen Ausschnitt ein.\";\n            return;\n        }\n        readSeq += 1;\n        var mine = readSeq;\n        var fr = new FileReader();\n        fr.onload = function () {\n            if (mine !== readSeq) { return; }\n            PTS.value = decodeBytes(fr.result);\n            update();\n        };\n        fr.onerror = function () {\n            if (mine !== readSeq) { return; }\n            PSTATUS.textContent = \"Die Datei lie\u00df sich nicht lesen. F\u00fcg die Zeilen direkt ins Feld ein.\";\n        };\n        fr.readAsArrayBuffer(file);\n    }\n    FILEBTN.addEventListener(\"click\", function () { FILEIN.click(); });\n    FILEIN.addEventListener(\"change\", function () {\n        var f = FILEIN.files.length > 0 ? FILEIN.files[0] : null;\n        FILEIN.value = \"\";\n        if (f) { readFile(f); }\n    });\n    function isFileDrag(e) {\n        var types = e.dataTransfer ? e.dataTransfer.types : null;\n        var q;\n        if (!types) { return false; }\n        for (q = 0; q < types.length; q += 1) {\n            if (types[q] === \"Files\") { return true; }\n        }\n        return false;\n    }\n    [\"dragenter\", \"dragover\"].forEach(function (ev) {\n        PTS.addEventListener(ev, function (e) {\n            if (isFileDrag(e)) {\n                e.preventDefault();\n                PTS.classList.add(\"is-over\");\n            }\n        });\n    });\n    PTS.addEventListener(\"dragleave\", function () { PTS.classList.remove(\"is-over\"); });\n    PTS.addEventListener(\"drop\", function (e) {\n        PTS.classList.remove(\"is-over\");\n        var fl = e.dataTransfer ? e.dataTransfer.files : null;\n        if (fl ? fl.length > 0 : false) {\n            e.preventDefault();\n            readFile(fl[0]);\n        }\n    });\n\n    \/\/ ---- Layout-Klasse (ab 760 px Boxbreite zwei Spalten) ----\n    function layoutPass() {\n        var wide = ROOT.getBoundingClientRect().width >= 760;\n        if (INPUTS.classList.contains(\"is-wide\") !== wide) { INPUTS.classList.toggle(\"is-wide\", wide); }\n    }\n\n    \/\/ ---- Start ----\n    ROOT.__skTest = { match: match, firstElement: firstElement, parseKeys: parseKeys, parsePoints: parsePoints };\n    layoutPass();\n    update();\n    if (window.ResizeObserver) {\n        var ro = new window.ResizeObserver(function () { layoutPass(); });\n        ro.observe(ROOT);\n    }\n    window.addEventListener(\"resize\", layoutPass);\n    window.addEventListener(\"load\", layoutPass);\n})();\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>Schl\u00fcssel-Check F\u00fcg deinen Beschreibungsschl\u00fcsselsatz und die Kurzbeschreibungen deiner Punkte ein. Du siehst sofort, welche Codes keinen Schl\u00fcssel treffen. Deine Daten [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"disabled","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[56],"tags":[],"class_list":["post-2807","post","type-post","status-publish","format-standard","hentry","category-punkte"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false},"uagb_author_info":{"display_name":false,"author_link":"https:\/\/cad-kompass.de\/?author=1"},"uagb_comment_info":0,"uagb_excerpt":"Schl\u00fcssel-Check F\u00fcg deinen Beschreibungsschl\u00fcsselsatz und die Kurzbeschreibungen deiner Punkte ein. Du siehst sofort, welche Codes keinen Schl\u00fcssel treffen. Deine Daten [&hellip;]","_links":{"self":[{"href":"https:\/\/cad-kompass.de\/index.php?rest_route=\/wp\/v2\/posts\/2807","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cad-kompass.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cad-kompass.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cad-kompass.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cad-kompass.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2807"}],"version-history":[{"count":2,"href":"https:\/\/cad-kompass.de\/index.php?rest_route=\/wp\/v2\/posts\/2807\/revisions"}],"predecessor-version":[{"id":2816,"href":"https:\/\/cad-kompass.de\/index.php?rest_route=\/wp\/v2\/posts\/2807\/revisions\/2816"}],"wp:attachment":[{"href":"https:\/\/cad-kompass.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2807"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cad-kompass.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2807"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cad-kompass.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2807"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}