Test

Z Centrum Informatiky
Verze z 2. 7. 2026, 10:39, kterou vytvořil Plachy (diskuse | příspěvky) (pokus o vložení čistého HTML (https://ci.ujep.cz/index.php?title=Test))
Přejít na: navigace, hledání

<!doctype html>

<html lang="cs">

<head>

  <meta charset="utf-8">

  <meta name="viewport" content="width=device-width, initial-scale=1">

  <title>Generátor podpisu UJEP</title>

  <style>

    :root {

      --ujep-blue: #0998DA;

      --border: #d8dee4;

      --text: #1f2328;

      --muted: #57606a;

      --bg: #f6f8fa;

      --white: #ffffff;

      --success: #1a7f37;

      --error: #cf222e;

    }

    * {

      box-sizing: border-box;

    }

    body {

      margin: 0;

      padding: 0;

      font-family: Arial, Helvetica, sans-serif;

      color: var(--text);

      background: var(--bg);

    }

    header {

      background: var(--white);

      border-bottom: 1px solid var(--border);

      padding: 24px 32px;

    }

    header h1 {

      margin: 0 0 8px 0;

      font-size: 24px;

      line-height: 1.25;

    }

    header p {

      margin: 0;

      color: var(--muted);

      font-size: 14px;

      line-height: 1.5;

      max-width: 920px;

    }

    main {

      display: grid;

      grid-template-columns: minmax(320px, 420px) minmax(420px, 1fr);

      gap: 24px;

      padding: 24px 32px 40px 32px;

      max-width: 1280px;

      margin: 0 auto;

    }

    section {

      background: var(--white);

      border: 1px solid var(--border);

      border-radius: 12px;

      padding: 24px;

      box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);

    }

    h2 {

      margin: 0 0 18px 0;

      font-size: 18px;

      line-height: 1.3;

    }

    label {

      display: block;

      font-size: 13px;

      font-weight: 700;

      margin: 16px 0 6px 0;

    }

    label .optional {

      font-weight: 400;

      color: var(--muted);

    }

    input {

      width: 100%;

      border: 1px solid var(--border);

      border-radius: 8px;

      padding: 10px 12px;

      font-size: 14px;

      font-family: Arial, Helvetica, sans-serif;

      color: var(--text);

      background: #ffffff;

    }

    input:focus {

      outline: 3px solid rgba(9, 152, 218, 0.18);

      border-color: var(--ujep-blue);

    }

    input[readonly] {

      background: #f6f8fa;

      color: var(--muted);

    }

    .language-options {

      display: flex;

      flex-direction: column;

      gap: 8px;

      margin: 8px 0 4px 0;

      padding: 12px;

      border: 1px solid var(--border);

      border-radius: 8px;

      background: #ffffff;

    }

    .language-options label {

      display: flex;

      align-items: flex-start;

      gap: 8px;

      margin: 0;

      font-weight: 400;

      line-height: 1.35;

    }

    .language-options input[type="radio"] {

      width: auto;

      margin-top: 2px;

    }

    .hint {

      margin-top: 6px;

      color: var(--muted);

      font-size: 12px;

      line-height: 1.4;

    }

    .actions {

      display: flex;

      gap: 12px;

      align-items: center;

      flex-wrap: wrap;

      margin-top: 24px;

    }

    button {

      border: 0;

      border-radius: 8px;

      background: var(--ujep-blue);

      color: #ffffff;

      font-weight: 700;

      font-size: 14px;

      padding: 11px 16px;

      cursor: pointer;

    }

    button:hover {

      filter: brightness(0.95);

    }

    button.secondary {

      background: #6e7781;

    }

    #status {

      font-size: 13px;

      line-height: 1.4;

    }

    #status.success {

      color: var(--success);

    }

    #status.error {

      color: var(--error);

    }

    .preview-wrap {

      overflow: auto;

      padding: 16px;

      border: 1px dashed var(--border);

      border-radius: 10px;

      background: #ffffff;

    }

    .preview-note {

      margin: 0 0 14px 0;

      color: var(--muted);

      font-size: 13px;

      line-height: 1.4;

    }

    .source-box {

      margin-top: 16px;

    }

    textarea {

      width: 100%;

      min-height: 220px;

      border: 1px solid var(--border);

      border-radius: 8px;

      padding: 12px;

      font-family: Consolas, "Courier New", monospace;

      font-size: 12px;

      line-height: 1.45;

      color: #24292f;

      background: #f6f8fa;

      resize: vertical;

    }

    details {

      margin-top: 18px;

    }

    summary {

      cursor: pointer;

      color: var(--ujep-blue);

      font-weight: 700;

      font-size: 14px;

    }

    @media (max-width: 900px) {

      main {

        grid-template-columns: 1fr;

        padding: 18px;

      }

      header {

        padding: 20px 18px;

      }

    }

  </style>

</head>

<body>

  <header>

    <h1>Generátor podpisu UJEP | UJEP Signature Generator</h1>

    <p>

      Vyplňte kontaktní údaje. Náhled podpisu se aktualizuje automaticky. Tlačítko „Zkopírovat podpis“

      zkopíruje vykreslený podpis do schránky jako HTML, aby jej bylo možné vložit do editoru podpisu v GroupWise. | Enter your contact information. The signature preview updates automatically. The "Copy Signature" button

      copies the rendered signature to the clipboard as HTML so that you can paste it into the GroupWise signature editor.

    </p>

    <p>

      Verze | Version 26.01 (c) P. Raška, UJEP 2026

    </p>

  </header>

  <main>

    <section aria-labelledby="form-title">

      <h2 id="form-title">Údaje do podpisu | Signature info</h2>

      <label for="name">Jméno a příjmení | Full name</label>

      <input id="name" type="text" value="Jan Novák" autocomplete="name">

      <label for="position">Pozice | Position</label>

      <input id="position" type="text" value="referent">

      <label for="secondaryPosition">Druhá pozice <span class="optional">(nepovinné | optional)</span> | Secondary position <span class="optional">(optional)</span></label>

      <input id="secondaryPosition" type="text" value="" placeholder="např. koordinátor projektu | e.g. project coordinator">

      <label for="unit">Součást | Faculty, Division</label>

      <input id="unit" type="text" value="Ekonomický odbor">

      <label for="secondaryUnit">Druhá součást <span class="optional">(nepovinné | optional)</span> | Secondary faculty, division <span class="optional">(optional)</span></label>

      <input id="secondaryUnit" type="text" value="" placeholder="např. Centrum podpory | e.g. Support Centre">

      <label for="address1">Adresa 1 | Address 1 <span class="optional"> (nepovinné | optional) </label>

      <input id="address1" type="text" value="" placeholder="Pasteurova 3544/1, 400 96 Ústí nad Labem">

      <label for="address2">Adresa 2 | Address 2 <span class="optional"> (nepovinné | optional) </label>

      <input id="address2" type="text" value="" placeholder="např. Kancelář CS 1.11 | e.g. office CS 1.11">

      <label>Jazyk automatických položek | Language of automatic items</label>

      <div class="language-options" role="radiogroup" aria-label="Jazyk automatických položek">

        <label>

          <input type="radio" name="signatureLanguage" value="cs" checked>

          Čeština – název univerzity a právní doložka budou česky

        </label>

        <label>

          <input type="radio" name="signatureLanguage" value="en">

          English – university name and legal disclaimer will be in English

        </label>

      </div>

      <label for="university">Instituce | Institution</label>

      <input id="university" type="text" value="Univerzita J. E. Purkyně v Ústí nad Labem" readonly>

      <div class="hint">Toto pole se doplní automaticky podle zvoleného jazyka. | This field is filled in automatically according to the selected language.</div>

      <label for="country">Země | Country</label>

      <input id="country" type="text" value="" readonly>

      <div class="hint">V české variantě se země v podpisu nezobrazuje. V anglické variantě se automaticky doplní Czechia. | In the Czech version, the country is not displayed. In the English version, Czechia is added automatically.</div>

      <label for="email">E-mail</label>

      <input id="email" type="email" value="jan.novak@ujep.cz" autocomplete="email">

      <label for="phone">Tel.<span class="optional"> (nepovinné | optional)</label>

      <input id="phone" type="text" value="+420 123 456 789" autocomplete="tel">

      <label for="phone2">Mobil/Cell<span class="optional"> (nepovinné | optional)</label>

      <input id="phone2" type="text" value="+420 987 654 321" autocomplete="cell">

      <label for="web">Web <span class="optional">(nepovinné | optional) </span></label>

      <input id="web" type="text" value="" placeholder="např. ujep.cz nebo https://www.ujep.cz/">

      <label for="orcid">ORCID iD <span class="optional">(nepovinné | optional) </span></label>

      <input id="orcid" type="text" value="" placeholder="0000-0000-0000-0000">

      <label for="researchGate">RG <span class="optional">(nepovinné | optional)</span></label>

      <input id="researchGate" type="text" value="" placeholder="https://www.researchgate.net/profile/Jmeno-Prijmeni">

      <label for="additionalInfo">Doplňující informace <span class="optional">(nepovinné)</span> | Additional information <span class="optional">(optional)</span></label>

      <input id="additionalInfo" type="text" value="" placeholder="více položek oddělte svislým znakem | divide more items with vertical line |">

      <label for="secondaryLogo">Další logo <span class="optional">(nepovinné)</span> | Additional logo <span class="optional">(optional)</span></label>

      <input id="secondaryLogo" type="text" value="" placeholder="vložte přímou URL obrázku | paste direct image URL">

      <div class="hint">Logo se zobrazí pouze při vyplnění URL. Maximální šířka v podpisu bude 100 px. | The logo will be displayed only if a URL is provided. Maximum width in the signature will be 100 px.</div>

      <label>

          <input id="includeDisclaimer" type="checkbox" checked style="width:auto; margin-right:6px;">

          Přidat právní doložku | Include legal disclaimer

      </label>

      <div class="actions">

        <button type="button" id="copyButton">Zkopírovat podpis | Copy signature </button>

        <button type="button" id="resetButton" class="secondary">Vrátit příklad | Back to example</button>

        <span id="status" aria-live="polite"></span>

      </div>

    </section>

    <section aria-labelledby="preview-title">

      <h2 id="preview-title">Náhled podpisu | Preview </h2>

      <p class="preview-note">

        Do GroupWise vkládejte podpis ideálně pomocí tlačítka „Zkopírovat podpis“. Pokud by prohlížeč kopírování HTML nepovolil,

        lze ručně označit náhled podpisu a zkopírovat jej pomocí Ctrl+C. | Ideally, insert your signature into GroupWise using the "Copy Signature" button.

        If your browser does not allow copying of HTML, you can manually select the signature preview and copy it using Ctrl+C.

      </p>

      <div class="preview-wrap">

        <div id="signaturePreview"></div>

      </div>

      <details>

        <summary>Zobrazit zdrojové HTML podpisu | View the source HTML of the signature </summary>

        <div class="source-box">

          <textarea id="htmlSource" readonly></textarea>

        </div>

      </details>

    </section>

  </main>

  <script>

    const LOGO_URL = "https://www.ujep.cz/wp-content/themes/ujep/img-ujep/logo.png";

    const UJEP_URL = "https://www.ujep.cz/";

    const UNIVERSITY_NAMES = {

      cs: "Univerzita J. E. Purkyně v Ústí nad Labem",

      en: "J. E. Purkyně University in Ústí nad Labem"

    };

    const COUNTRY_NAMES = {

      cs: "",

      en: "Czechia"

    };

    const DISCLAIMERS = {

      cs: "Tato e-mailová zpráva včetně všech příloh je určena výhradně osobě, které je adresována a její obsah může být důvěrný nebo chráněný právními předpisy. Pokud nejste zamýšleným příjemcem, tuto zprávu nepoužívejte, nezveřejňujte, nekopírujte, nešiřte, ani s ní jinak nenakládejte. Prosím neprodleně informujte odesílatele a zprávu vymažte. Odesílatel uvádí, že pokud je tato zpráva součástí právního jednání závazkového, zejména obchodního charakteru a není v textu zprávy výslovně uvedeno jinak, je tato zpráva pouhou informací. Tato zpráva není jednáním o smlouvě a nezakládá předsmluvní odpovědnost odesílatele. Obsahuje-li tato zpráva nebo některá z jejích příloh osobní údaje, dbejte při jejich dalším zpracování (zejména při archivaci) souladu s pravidly evropského nařízení GDPR.",

      en: "This email message, including any attachments, is intended solely for the person to whom it is addressed and may contain confidential information or information protected by law. If you are not the intended recipient, do not use, disclose, copy, distribute, or otherwise handle this message. Please notify the sender immediately and delete the message. The sender states that if this message forms part of a legal act, particularly of a contractual or commercial nature, and unless explicitly stated otherwise in the text of the message, this message is for informational purposes only. This message does not constitute a contractual offer and does not give rise to any pre-contractual liability on the part of the sender. If this message or any of its attachments contains personal data, please ensure that any further processing (especially archiving) complies with the rules of the European GDPR regulation."

    };

    const fields = {

      name: document.getElementById("name"),

      position: document.getElementById("position"),

      secondaryPosition: document.getElementById("secondaryPosition"),

      unit: document.getElementById("unit"),

      secondaryUnit: document.getElementById("secondaryUnit"),

      university: document.getElementById("university"),

      address1: document.getElementById("address1"),

      address2: document.getElementById("address2"),     

      country: document.getElementById("country"),

      email: document.getElementById("email"),

      phone: document.getElementById("phone"),

      phone2: document.getElementById("phone2"),

      web: document.getElementById("web"),

      orcid: document.getElementById("orcid"),

      researchGate: document.getElementById("researchGate"),

      additionalInfo: document.getElementById("additionalInfo"),

      secondaryLogo: document.getElementById("secondaryLogo"),

      includeDisclaimer: document.getElementById("includeDisclaimer")

    };

    const languageRadios = document.querySelectorAll('input[name="signatureLanguage"]');

    const preview = document.getElementById("signaturePreview");

    const htmlSource = document.getElementById("htmlSource");

    const statusEl = document.getElementById("status");

    const copyButton = document.getElementById("copyButton");

    const resetButton = document.getElementById("resetButton");

    function escapeHtml(value) {

      return String(value || "")

        .replace(/&/g, "&amp;")

        .replace(/</g, "&lt;")

        .replace(/>/g, "&gt;")

        .replace(/"/g, "&quot;")

        .replace(/'/g, "&#039;");

    }

    function normalizeUrl(value) {

      const trimmed = String(value || "").trim();

      if (!trimmed) return "";

      if (/^https?:\/\//i.test(trimmed)) return trimmed;

      return "https://" + trimmed;

    }

    function visibleWebText(value) {

      return String(value || "").trim().replace(/^https?:\/\//i, "").replace(/\/$/, "");

    }

    function normalizeOrcid(value) {

      const trimmed = String(value || "").trim();

      if (!trimmed) return "";

    return trimmed

      .replace(/^https?:\/\/orcid\.org\//i, "")

      .replace(/^orcid\.org\//i, "")

      .replace(/\s+/g, "");

    }

    function buildOrcidUrl(value) {

      const id = normalizeOrcid(value);

      return id ? "https://orcid.org/" + id : "";

    }

    function looksLikeUrl(value) {

      const trimmed = String(value || "").trim();

      return /^https?:\/\//i.test(trimmed) || /^[a-z0-9.-]+\.[a-z]{2,}(\/.*)?$/i.test(trimmed);

    }

    function getSelectedLanguage() {

      const selected = document.querySelector('input[name="signatureLanguage"]:checked');

      return selected ? selected.value : "cs";

    }

    function buildSignatureHtml() {

      const language = getSelectedLanguage();

      const name = escapeHtml(fields.name.value.trim());

      const position = escapeHtml(fields.position.value.trim());

      const secondaryPosition = escapeHtml(fields.secondaryPosition.value.trim());

      const unit = escapeHtml(fields.unit.value.trim());

      const secondaryUnit = escapeHtml(fields.secondaryUnit.value.trim());

      const university = escapeHtml(UNIVERSITY_NAMES[language]);  

      const address1 = escapeHtml(fields.address1.value.trim());

      const address2 = escapeHtml(fields.address2.value.trim());

      const country = escapeHtml(COUNTRY_NAMES[language]);

      const email = escapeHtml(fields.email.value.trim());

      const phone = escapeHtml(fields.phone.value.trim());

      const phone2 = escapeHtml(fields.phone2.value.trim());

      const webRaw = fields.web.value.trim();

      const orcidRaw = fields.orcid.value.trim();

      const researchGateRaw = fields.researchGate.value.trim();

      const additionalInfoRaw = fields.additionalInfo.value.trim();

      const secondaryLogoRaw = fields.secondaryLogo.value.trim();

      const includeDisclaimer = fields.includeDisclaimer.checked;

      const webUrl = normalizeUrl(webRaw);

      const webText = escapeHtml(visibleWebText(webRaw));

      const normalizedOrcid = normalizeOrcid(orcidRaw);

      const orcidLink = buildOrcidUrl(orcidRaw);

      const orcidText = escapeHtml(normalizedOrcid);

      const secondaryPositionLine = secondaryPosition

        ? `<br>${secondaryPosition}`

        : "";

      const secondaryUnitLine = secondaryUnit

        ? `<br>${secondaryUnit}`

        : "";

      const address1Line = address1

        ? `${address1}<br>`

        : "";

      const address2Line = address2

        ? `${address2}<br>`

        : "";

      const countryLine = country

        ? `${country}<br>`

        : "";

      const webLine = webRaw

        ? `web: <a href="${escapeHtml(webUrl)}" target="_blank" style="color:#0000ee; text-decoration:underline;">${webText}</a><br>`

        : "";

      const orcidLine = normalizedOrcid

        ? `ORCID: <a href="${escapeHtml(orcidLink)}" target="_blank" style="color:#0000ee; text-decoration:underline;">${orcidText}</a><br>`

        : "";

      const researchGateLine = researchGateRaw

        ? `RG: <a href="${escapeHtml(normalizeUrl(researchGateRaw))}" target="_blank" style="color:#0000ee; text-decoration:underline;"> ResearchGate</a><br>`

        : "";

      const additionalInfoLine = additionalInfoRaw

        ? additionalInfoRaw

        .split("|")

        .map(item => item.trim())

        .filter(item => item.length > 0)

        .map(item => looksLikeUrl(item)

        ? `<a href="${escapeHtml(normalizeUrl(item))}" target="_blank" style="color:#0000ee; text-decoration:underline;">${escapeHtml(item)}</a><br>`

        : `${escapeHtml(item)}<br>`

        )

        .join("")

        : "";

      const secondaryLogoBlock = secondaryLogoRaw

        ? `<div style="margin:0 0 22px 0;">

        <img src="${escapeHtml(normalizeUrl(secondaryLogoRaw))}"

         alt="Additional logo"

         width="100"

         style="display:block; max-width:100px; height:auto; border:0; outline:none; text-decoration:none;">

        </div>`

        : "";

      const emailLine = email

        ? `e-mail: <a href="mailto:${email}" style="color:#0000ee; text-decoration:underline;">${email}</a><br>`

        : "e-mail:<br>";

      const phoneLine = phone

        ? `Tel: ${phone}<br>`

        : "";

      const phone2Line = phone2

        ? `Mobil/Cell ${phone2}<br>`

        : "";

      const disclaimerBlock = includeDisclaimer

        ? `<div style="font-size:10px; line-height:1.25; color:#777777; max-width:760px;">

        ${escapeHtml(DISCLAIMERS[language])}

        </div>`

        : "";

      return `

<div style="font-family: Arial, Helvetica, sans-serif; color:#000000; font-size:14px; line-height:1.35; max-width:780px;">

  <div style="font-size:12px; line-height:1.35; margin:0 0 18px 0;">

    <strong>${name || "Jméno Příjmení"}</strong><br>

    ${position || "Pozice"}${secondaryPositionLine}

  </div>

  <div style="font-size:12px; line-height:1.35; margin:0 0 24px 0;">

    ${unit || "Součást"}${secondaryUnitLine}<br>

    ${university}<br>

    ${address1Line}

    ${address2Line}

    ${countryLine}

    ${emailLine}

    ${phoneLine}

    ${phone2Line}

    ${webLine}

    ${orcidLine}

    ${researchGateLine}

    ${additionalInfoLine}

  </div>

  <div style="margin:0 0 22px 0;">

    <a href="${UJEP_URL}" target="_blank" style="text-decoration:none;">

      <img src="${LOGO_URL}"

           alt="Univerzita Jana Evangelisty Purkyně v Ústí nad Labem"

           width="150"

           style="display:block; border:0; outline:none; text-decoration:none;">

    </a>

  </div>

  ${secondaryLogoBlock}

  ${disclaimerBlock}

</div>`.trim();

    }

    function updatePreview() {

      const language = getSelectedLanguage();

      fields.university.value = UNIVERSITY_NAMES[language];

      fields.country.value = COUNTRY_NAMES[language];

      const signatureHtml = buildSignatureHtml();

      preview.innerHTML = signatureHtml;

      htmlSource.value = signatureHtml;

      statusEl.textContent = "";

      statusEl.className = "";

    }

    async function copySignature() {

      const signatureHtml = buildSignatureHtml();

      const plainText = preview.innerText;

      try {

        if (navigator.clipboard && window.ClipboardItem) {

          const htmlBlob = new Blob([signatureHtml], { type: "text/html" });

          const textBlob = new Blob([plainText], { type: "text/plain" });

          await navigator.clipboard.write([

            new ClipboardItem({

              "text/html": htmlBlob,

              "text/plain": textBlob

            })

          ]);

        } else {

          const range = document.createRange();

          range.selectNodeContents(preview);

          const selection = window.getSelection();

          selection.removeAllRanges();

          selection.addRange(range);

          const successful = document.execCommand("copy");

          selection.removeAllRanges();

          if (!successful) throw new Error("Kopírování přes execCommand se nezdařilo. | Kopírování přes execCommand se nezdařilo.");

        }

        statusEl.textContent = "Podpis byl zkopírován do schránky. | The signature has been copied to the clipboard.";

        statusEl.className = "success";

      } catch (error) {

        const range = document.createRange();

        range.selectNodeContents(preview);

        const selection = window.getSelection();

        selection.removeAllRanges();

        selection.addRange(range);

        statusEl.textContent = "Automatické kopírování se nezdařilo. Náhled je označený; stiskněte Ctrl+C. | Automatic copying failed. The preview is selected; press Ctrl+C.";

        statusEl.className = "error";

      }

    }

    function resetExample() {

      fields.name.value = "Jan Novák";

      fields.position.value = "referent";

      fields.secondaryPosition.value = "";

      fields.unit.value = "Ekonomický odbor";

      fields.secondaryUnit.value = "";

      document.querySelector('input[name="signatureLanguage"][value="cs"]').checked = true;

      fields.university.value = UNIVERSITY_NAMES.cs;

      fields.address1.value = "";     

      fields.address2.value = "";     

      fields.country.value = COUNTRY_NAMES.cs;

      fields.email.value = "jan.novak@ujep.cz";

      fields.phone.value = "+420 123 456 789";

      fields.phone2.value = "+420 987 654 321";

      fields.web.value = "ujep.cz";

      fields.orcid.value = "";

      fields.additionalInfo.value = "";

      fields.secondaryLogo.value = "";

      updatePreview();

    }

    Object.values(fields).forEach((field) => {

      field.addEventListener("input", updatePreview);

    });

    languageRadios.forEach((radio) => {

      radio.addEventListener("change", updatePreview);

    });

    copyButton.addEventListener("click", copySignature);

    resetButton.addEventListener("click", resetExample);

    updatePreview();

  </script>

</body>

</html>