{
  "name": "anketa copy to contract",
  "code": "(() => {\n  // ====== settings ======\n  const BTN_ID = 'ks54-copy-contract-btn';\n  const LOG = '[Anketa→договор]';\n\n  if (window.__KS54_ANKETA_COPY__) return;\n  window.__KS54_ANKETA_COPY__ = true;\n\n  if (window.top !== window.self) return;\n\n  // ====== form helpers (редактирование: input/select; просмотр: div[name]) ======\n  function anketaRoot() {\n    return document.getElementById('anketa');\n  }\n\n  function fieldEl(nameOrId) {\n    const root = anketaRoot();\n    if (!root) return document.getElementById(nameOrId);\n    return (\n      document.getElementById(nameOrId) ||\n      root.querySelector('[name=\"' + nameOrId.replace(/\"/g, '\\\\\"') + '\"]')\n    );\n  }\n\n  function fieldText(el) {\n    if (!el) return '';\n    const tag = (el.tagName || '').toUpperCase();\n\n    if (tag === 'SELECT') {\n      if (el.selectedIndex >= 0) {\n        const opt = el.options[el.selectedIndex];\n        const txt = opt ? String(opt.text || opt.value || '').trim() : '';\n        if (txt && !/^\\(не определено\\)$/i.test(txt)) return txt;\n      }\n      return String(el.value || '').trim();\n    }\n\n    if (tag === 'INPUT' || tag === 'TEXTAREA') {\n      return String(el.value ?? '').trim();\n    }\n\n    let t = String(el.textContent || el.innerText || '')\n      .replace(/\\u00a0/g, ' ')\n      .replace(/\\s+/g, ' ')\n      .trim();\n    if (/^\\(не определено\\)$/i.test(t)) return '';\n    return t;\n  }\n\n  function val(nameOrId) {\n    return fieldText(fieldEl(nameOrId));\n  }\n\n  function selectedText(selId) {\n    return val(selId);\n  }\n\n  function normDate(s) {\n    s = String(s || '').trim();\n    if (!s) return '';\n    if (/^\\d{4}-\\d{2}-\\d{2}$/.test(s)) {\n      const [y, m, d] = s.split('-');\n      return `${d}.${m}.${y}`;\n    }\n    if (/^\\d{2}\\.\\d{2}\\.\\d{4}$/.test(s)) return s;\n    return s;\n  }\n\n  function normSNILS(s) {\n    const d = String(s || '')\n      .replace(/\\D/g, '')\n      .slice(0, 11);\n    if (d.length !== 11) return String(s || '').trim();\n    return `${d.slice(0, 3)}-${d.slice(3, 6)}-${d.slice(6, 9)} ${d.slice(9)}`;\n  }\n\n  function normKod(s) {\n    const d = String(s || '')\n      .replace(/\\D/g, '')\n      .slice(0, 6);\n    if (d.length !== 6) return String(s || '').trim();\n    return `${d.slice(0, 3)}-${d.slice(3)}`;\n  }\n\n  function splitFIO(fio) {\n    const p = String(fio || '')\n      .trim()\n      .split(/\\s+/);\n    return { family: p[0] || '', name: p[1] || '', surname: p.slice(2).join(' ') || '' };\n  }\n\n  function extractProgramCode(raw) {\n    const m = String(raw || '').match(/\\d{2}\\.\\d{2}\\.\\d{2}/);\n    return m ? m[0] : '';\n  }\n\n  function cutContractNo(s) {\n    const m = String(s || '').match(/^\\s*(\\d{1,6})\\s*\\/\\s*\\d{2,4}\\s*$/);\n    return m ? m[1] : String(s || '');\n  }\n\n  function buildStudentAddress() {\n    const parts = [];\n    const city = val('address_register_city');\n    const street = val('address_register');\n    const bld = val('address_register_bld');\n    const flat = val('address_register_flat');\n    if (city) parts.push(`г. ${city}`);\n    if (street) parts.push(street);\n    if (bld) parts.push(`д. ${bld}`);\n    if (flat) parts.push(`кв. ${flat}`);\n    return parts.join(', ');\n  }\n\n  function isPayVB(raw) {\n    const t = String(raw || '').trim().toLowerCase();\n    return t === 'vb' || t.includes('внебюджет') || t.includes('хозрасч');\n  }\n\n  function pickVBSpecIndex() {\n    for (let i = 1; i <= 6; i++) {\n      const ap = i === 1 ? '' : String(i);\n      const spec = val('spo_spec' + ap);\n      if (spec && isPayVB(val('pay_level' + ap))) return i;\n    }\n    return 1;\n  }\n\n  function mapStudyForm(ed_type) {\n    const t = String(ed_type || '').trim().toLowerCase();\n    if (t === 'o' || t.startsWith('очная')) return 'очная';\n    if (t === 'zo' || t.startsWith('заочная')) return 'заочная';\n    if (t === 'oz' || t.includes('очно-заоч')) return 'очно-заочная';\n    return '';\n  }\n\n  function mapBaseEdu(ed_level) {\n    const t = String(ed_level || '').trim();\n    if (t === 'SCH9' || /9\\s*кл/i.test(t) || t.includes('9 класс')) return '9 кл.';\n    if (t === 'SCH11' || /11\\s*кл/i.test(t) || t.includes('11 класс')) return '11 кл.';\n    return '';\n  }\n\n  function mapSchedule(code) {\n    const t = String(code || '').trim().toLowerCase();\n    if (t === '1mes' || t.includes('ежемесяч')) return 'monthly';\n    if (t === '1sem' || t.includes('семестр')) return 'semester';\n    if (t === '1year' || t.includes('ежегод')) return 'yearly';\n    if (t === 'full' || t.includes('весь период') || t.includes('за весь')) return 'full';\n    return '';\n  }\n\n  function mapDocType(t) {\n    const s = String(t || '').trim().toLowerCase();\n    if (s === 'pass' || s.includes('паспорт')) return 'паспорт';\n    return s ? 'иной' : '';\n  }\n\n  function isAttestatOriginal(raw) {\n    const t = String(raw || '').trim().toLowerCase();\n    return t === 'orig' || t === 'оригинал' || t.startsWith('оригинал');\n  }\n\n  // ====== payload ======\n  function buildPayload() {\n    const specIdx = pickVBSpecIndex();\n    const ap = specIdx === 1 ? '' : String(specIdx);\n    const rawSpec = val('spo_spec' + ap);\n    const programCode = extractProgramCode(rawSpec);\n    const signerName = selectedText('dogovor_sign_by');\n    const pfPay = val('dogovor_pay_by_pfr').toLowerCase() === 'да' ? 'да' : '';\n    const attDate = val('date_attestat');\n    const attestat = attDate && isAttestatOriginal(val('att_orig')) ? 1 : 0;\n\n    const contract = {\n      number: cutContractNo(val('dogov_no')),\n      date: normDate(val('dogov_date')),\n      signer: signerName,\n    };\n\n    const program = {\n      code: programCode,\n      label: rawSpec || '',\n      study_form: mapStudyForm(val('ed_type' + ap)),\n      base: mapBaseEdu(val('ed_level')),\n      schedule: mapSchedule(val('dogovor_pay_type')),\n      year_price: '',\n      total_price: '',\n    };\n\n    const student = {\n      family: val('surname'),\n      name: val('name'),\n      surname: val('father'),\n      birth: normDate(val('date_birth')),\n      address: buildStudentAddress(),\n      doc_type: mapDocType(val('pasp_type')),\n      doc_series: val('pasp_ser'),\n      doc_number: val('pasp_no'),\n      doc_issuer: val('pasp_vydan'),\n      doc_date: normDate(val('pasp_date')),\n      doc_kod: normKod(val('pasp_code')),\n      phone: val('phone_mob'),\n      email: val('email'),\n      snils: normSNILS(val('snils')),\n      inn: val('person_inn'),\n    };\n\n    const fioPayer = splitFIO(val('payer_fio'));\n    const customer = {\n      family: fioPayer.family,\n      name: fioPayer.name,\n      surname: fioPayer.surname,\n      birth: normDate(val('payer_date_birth')),\n      address: val('payer_address'),\n      doc_type: mapDocType(val('payer_pasp_type')),\n      doc_series: val('payer_pasp_ser'),\n      doc_number: val('payer_pasp_no'),\n      doc_issuer: val('payer_pasp_vydan'),\n      doc_date: normDate(val('payer_pasp_date')),\n      doc_kod: normKod(val('payer_pasp_code')),\n      phone: val('payer_phone'),\n      email: val('payer_email'),\n      snils: normSNILS(val('payer_insurance_number')),\n      inn: val('payer_inn'),\n    };\n\n    return {\n      contract,\n      customer,\n      student,\n      program,\n      attestat,\n      pf_pay: pfPay,\n      _meta: { source: 'anketa', specIndex: specIdx, href: location.href, ts: Date.now() },\n    };\n  }\n\n  // ====== clipboard ======\n  async function copyText(text) {\n    if (navigator.clipboard?.writeText) {\n      try {\n        await navigator.clipboard.writeText(text);\n        return;\n      } catch (_) {\n        /* fallback */\n      }\n    }\n    const ta = document.createElement('textarea');\n    ta.value = text;\n    ta.style.cssText = 'position:fixed;left:-9999px;top:0';\n    document.body.appendChild(ta);\n    ta.focus();\n    ta.select();\n    const ok = document.execCommand('copy');\n    ta.remove();\n    if (!ok) throw new Error('copy failed');\n  }\n\n  function notify(msg) {\n    alert(msg);\n    console.log(LOG, msg);\n  }\n\n  async function copyPayload() {\n    const payload = buildPayload();\n    const text = JSON.stringify(payload, null, 2);\n    try {\n      await copyText(text);\n      notify('Данные для договора скопированы в буфер обмена.');\n      return { ok: true, payload };\n    } catch (e) {\n      console.error(LOG, e);\n      const manual = prompt('Скопируйте вручную и нажмите OK:', text);\n      if (manual != null) return { ok: true, payload };\n      notify('Не удалось скопировать');\n      return { ok: false };\n    }\n  }\n\n  // ====== UI ======\n  function mountButton() {\n    const host = document.getElementById('anketa');\n    if (!host) return false;\n    if (document.getElementById(BTN_ID)) return true;\n\n    const btn = document.createElement('button');\n    btn.type = 'button';\n    btn.id = BTN_ID;\n    btn.textContent = 'Скопировать в договор';\n    btn.className = 'medium b cblue';\n    btn.style.margin = '10px 0';\n    btn.style.padding = '10px 14px';\n    btn.addEventListener('click', () => copyPayload());\n\n    try {\n      const row = host.children[0]?.children[0];\n      if (row) row.appendChild(btn);\n      else host.prepend(btn);\n    } catch (_) {\n      host.prepend(btn);\n    }\n    return true;\n  }\n\n  function boot() {\n    if (!mountButton()) {\n      const obs = new MutationObserver(() => {\n        if (mountButton()) obs.disconnect();\n      });\n      obs.observe(document.documentElement, { childList: true, subtree: true });\n      setTimeout(() => obs.disconnect(), 30_000);\n    }\n    console.log(LOG, 'loaded');\n  }\n\n  window.__ANKETA_COPY__ = {\n    buildPayload,\n    copyPayload,\n    mountButton,\n  };\n\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', boot, { once: true });\n  } else {\n    boot();\n  }\n})();",
  "runCondition": "url",
  "matchType": "contains",
  "conditionKey": "https://ais.ks54.ru",
  "trigger": "automatic",
  "autoTiming": "onLoad",
  "enabled": true
}
