Quest: Strona z bazą JSON (ładowaną w JS)

A gdyby tak utworzyć plik z bazą danych…?

Poniższy projekcik to strona której treść jest pobierana z pliku JSON za pomocą skryptu JS. Uruchom projekt i popraw:

  1. W pliku JSON są podane adresy grafik, jednak grafiki nie istnieją. Przygotuj je i spraw, by się wyświetlały.
  2. Strona startowa nie ładuje się poprawnie. Napraw to.
  3. Popraw wygląd całej strony. Dobrze by było, gdyby nie przestał działać JS 🙂

Baza JSON

{
  "podstrony": [
    {
      "tytul": "Życie wilków w Polsce",
      "adres": "drapiezniki/wilki",
      "tresc": [
        "Wilki są zwierzętami społecznymi, żyjącymi w zorganizowanych grupach rodzinnych zwanych watahami. Wataha ma zazwyczaj jedną parę reprodukcyjną.",
        "Ich terytorium może obejmować setki kilometrów kwadratowych. Komunikują się za pomocą wycia, znakowania terenu oraz języka ciała."
      ],
      "obraz": "obrazy/wilk.jpg"
    },
    {
      "tytul": "Ptaki wodne – kaczki",
      "adres": "ptaki/kaczki",
      "tresc": [
        "Kaczki to popularne ptaki wodne zamieszkujące jeziora, stawy i rzeki. Często można je spotkać w miejskich parkach, gdzie chętnie jedzą okruchy od ludzi.",
        "Potrafią pływać dzięki błoniastym stopom, a ich pióra mają właściwości wodoodporne. Samce często wyróżniają się bardziej barwnym upierzeniem."
      ],
      "obraz": "obrazy/kaczka.jpg"
    },
    {
      "tytul": "Wróble",
      "adres": "ptaki/wroble",
      "tresc": [
        "Nie wiem nic o wróblach, ale to takie miłe ptaszki",
        "....."
      ],
      "obraz": "obrazy/wrobel.jpg"
    },
    {
      "tytul": "Jeże i ich zwyczaje",
      "adres": "ssaki/jeze",
      "tresc": [
        "Jeże to niewielkie ssaki, które prowadzą nocny tryb życia. Zwijają się w kulkę, aby chronić się przed drapieżnikami, wykorzystując swoje ostre kolce.",
        "Zimą zapadają w hibernację. Żywią się owadami, ślimakami i małymi bezkręgowcami, pomagając kontrolować ich populacje w ogrodach."
      ],
      "obraz": "obrazy/jez.jpg"
    },
    {
      "tytul": "Zwyczaje wiewiórek",
      "adres": "gryzonie/wiewiorki",
      "tresc": [
        "Wiewiórki są aktywne głównie w ciągu dnia, szczególnie rano i późnym popołudniem. Uwielbiają wspinać się po drzewach i przeskakiwać z gałęzi na gałąź.",
        "Gromadzą pożywienie na zimę, chowając orzechy i nasiona w różnych miejscach. Ich pamięć jest zaskakująco dobra, co pozwala im odnaleźć skrytki wiele tygodni później."
      ],
      "obraz": "obrazy/wiewiorka.jpg"
    },
    {
      "tytul": "Myszy",
      "adres": "gryzonie/myszy",
      "tresc": [
        "Nie wiem nic o myszach, ale to takie miłe potwory",
        "....."
      ],
      "obraz": "obrazy/mysz.jpg"
    }
  ]
}

Plik index.html

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <title>Sekcje w HTML5</title>
    <meta name="keywords" content="main, nav, aside, article">
    <meta name="description" content="Strona o zwierzętach w Polsce z dynamicznymi podstronami">
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="kontener">
        <header>
            <h1>Zwierzaki w Polsce</h1>
        </header>
        <div>
            <main>
                <article>
                    <h2>Witaj!</h2>
                    <p>Wybierz zwierzę z menu po prawej stronie, aby dowiedzieć się więcej.</p>
                </article>
            </main>
            <aside>
                <nav>
                    <h3>Menu</h3>
                    <ul id="menu">
                        <li><a href="#">Strona Główna</a></li>
                    </ul>
                </nav>
                <section>
                    <h3>Reklamy</h3>
                    <p>Lorem ipsum dolor sit emet...</p>
                </section>
                <section>
                    <h3>Social media</h3>
                    <p>Znajdź nas na Instagramie i Facebooku!</p>
                </section>
            </aside>
        </div>
        <footer>
            <div> pk.sth © 2025</div>
            <div>Projekt edukacyjny o faunie Polski.</div>
        </footer>
    </div>
    <script src="script.js"></script>
</body>
</html>

Styl CSS

@media only screen and (min-width: 900px) {
    #kontener {
        width: 850px;
        margin: 0 auto 0 auto;
    }
    main {
        float: left;
        width: 70%;
    }
    aside {
        float: right;
        width: 28%;
    }
    footer {
        clear: both;
    }
}

Skrypcik JS

document.addEventListener("DOMContentLoaded", function () {
    const navList = document.getElementById("menu");
    const main = document.querySelector("main");

    function loadJSON(callback) {
        fetch("baza.json")
            .then(res => res.json())
            .then(data => callback(data))
            .catch(err => console.error("Błąd wczytywania JSON:", err));
    }

    function buildMenu(data) {
        data.podstrony.forEach(p => {
            const li = document.createElement("li");
            const a = document.createElement("a");
            a.href = "#" + p.adres;
            a.textContent = p.tytul;
            li.appendChild(a);
            navList.appendChild(li);
        });
    }

    function showPage(data, hash) {
        const adres = hash.replace(/^#/, '');
        const p = data.podstrony.find(p => p.adres === adres);

        if (p) {
            main.innerHTML = `
                <article>
                    <h2>${p.tytul}</h2>
                    ${p.tresc}
                    <img src="${p.obraz}" alt="${p.tytul}" style="max-width: 100%; margin-top: 10px;">
                </article>
            `;
        } else {
            main.innerHTML = `
                <article>
                    <h2>Nie znaleziono strony</h2>
                    <p>Podana podstrona nie istnieje lub została usunięta.</p>
                </article>
            `;
        }
    }

    function init(data) {
        buildMenu(data);
        if (window.location.hash) {
            showPage(data, window.location.hash);
        }

        window.addEventListener("hashchange", () => {
            showPage(data, window.location.hash);
        });
    }

    loadJSON(init);
});

.

Quest: Szyfry w JS

Szyfry podstawieniowe są łatwe do zrozumienia (i łatwe do złamania). Przetestuj te szyrowania i zrozum ich algorytmy. Zaszyfruj na kartce zdanie „To be or not to be” w ROT13, GADERYPOLUKI.

Szyfrowanie #1

Szyfr Cezara to szyfr podstawieniowy. Kod JS wykorzystuje numeryczne kody liter (ASCI). Dawniej napisano by go bez użycia kodów ASCI. Wygeneruj taki kod (np dla ROT13) i porównaj go z poniższym. Który jest łatwiejszy?

<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="UTF-8" />
  <title>Szyfr Cezara</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    textarea { width: 100%; height: 80px; }
    select, input, button { margin: 10px 0; }
  </style>
</head>
<body>
  <h1>Szyfr Cezara</h1>

  <label>Tekst:</label><br>
  <textarea id="inputText"></textarea><br>

  <label>Przesunięcie (1-25):</label><br>
  <input type="number" id="shift" min="1" max="25" value="3"><br>

  <label>Tryb:</label><br>
  <select id="mode">
    <option value="encode">Zakoduj</option>
    <option value="decode">Odszyfruj</option>
  </select><br>

  <button id="runButton">Wykonaj</button>

  <h2>Wynik:</h2>
  <textarea id="outputText" readonly></textarea>

  <script>
    function caesar(str, shift, mode) {
      return str.split('').map(char => {
        const code = char.charCodeAt(0);

        const shiftAmount = mode === 'decode' ? -shift : shift;

        if (char >= 'A' && char <= 'Z') {
          return String.fromCharCode(((code - 65 + shiftAmount + 26) % 26) + 65);
        }
        else if (char >= 'a' && char <= 'z') {
          return String.fromCharCode(((code - 97 + shiftAmount + 26) % 26) + 97);
        }
        else {
          return char; // nie zmieniaj znaków specjalnych
        }
      }).join('');
    }

    document.getElementById('runButton').addEventListener('click', () => {
      const text = document.getElementById('inputText').value;
      const shift = parseInt(document.getElementById('shift').value);
      const mode = document.getElementById('mode').value;

      const result = caesar(text, shift, mode);
      document.getElementById('outputText').value = result;
    });
  </script>
</body>
</html>

Szyfrowanie #2

Szyfr GADERYPOLUKI to też szyfr podstawieniowy. Czym różni się Gaderypoluki od ROT13 w wersji nie wykorzystującej kodów ASCI?

<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="UTF-8">
  <title>Szyfr GADERYPOLUKI</title>
  <style>
    body {font-family: Arial, sans-serif; background: #f5f5f5; padding: 20px; max-width: 600px; margin: auto;}
    textarea {width: 100%; height: 100px; margin-bottom: 10px; font-size: 16px; }
    button { padding: 10px 20px; margin-right: 10px; background-color: #007acc; color: white; border: none; border-radius: 5px; cursor: pointer; }
    .result { margin-top: 20px; padding: 15px; background: white; border: 1px solid #ccc; border-radius: 8px; }
  </style>
</head>
<body>

  <h2>Szyfr GADERYPOLUKI</h2>
  <p>Wpisz tekst do zaszyfrowania lub odszyfrowania (litery A-Z):</p>

  <textarea id="inputText" placeholder="Wpisz tutaj..."></textarea><br>
  <button onclick="encrypt()">Zaszyfruj / Odszyfruj</button>

  <div class="result">
    <strong>Wynik:</strong>
    <p id="output"></p>
  </div>

  <script>
    const keyPairs = {
      'G': 'A', 'A': 'G',
      'D': 'E', 'E': 'D',
      'R': 'Y', 'Y': 'R',
      'P': 'O', 'O': 'P',
      'L': 'U', 'U': 'L',
      'K': 'I', 'I': 'K'
    };

    function gaderypoluki(text) {
      return text.toUpperCase().split('').map(char => {
        return keyPairs[char] || char;
      }).join('');
    }

    function encrypt() {
      const input = document.getElementById('inputText').value;
      const output = gaderypoluki(input);
      document.getElementById('output').innerText = output;
    }
  </script>

</body>
</html>

Szyfrowanie #3

Szyfr BIFID jest połączeniem szyfru podstawieniowego z szyfrem przestawieniowym. Zaszyfruj na kartce zdanie „To be or not to be” w BIFID. Przetestuj poniższy kod i ulepsz dodając obsługę małych liter, polskich znaków i cyfr. Powiększ tablicę polybiusSquare do 9×9. Dlaczego takie szyfrowanie jest „mocniejsze”?

<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="UTF-8">
  <title>Szyfrowanie Bifid - Demonstracja</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      padding: 20px;
    }
    input, button {
      margin: 5px 0;
      padding: 8px;
    }
  </style>
</head>
<body>
  <h2>Szyfrowanie Bifid</h2>
  <label for="inputText">Wprowadź wiadomość (tylko litery):</label><br>
  <input type="text" id="inputText" placeholder="np. TAJNAWIADOMOSC"><br>
  <button onclick="encryptBifid()">Szyfruj</button>
  <button onclick="decryptBifid()">Deszyfruj</button>
  <h3>Wynik:</h3>
  <p id="output"></p>

  <script>
    const polybiusSquare = [
      ['A', 'B', 'C', 'D', 'E'],
      ['F', 'G', 'H', 'I', 'K'], // J = I
      ['L', 'M', 'N', 'O', 'P'],
      ['Q', 'R', 'S', 'T', 'U'],
      ['V', 'W', 'X', 'Y', 'Z']
    ];

    function getCoordinates(letter) {
      if (letter === 'J') letter = 'I';
      for (let row = 0; row < 5; row++) {
        for (let col = 0; col < 5; col++) {
          if (polybiusSquare[row][col] === letter) {
            return [row + 1, col + 1]; // +1 dla indeksu 1-based
          }
        }
      }
      return null;
    }

    function getLetter(row, col) {
      return polybiusSquare[row - 1][col - 1];
    }

    function encryptBifid() {
      let input = document.getElementById("inputText").value.toUpperCase().replace(/[^A-Z]/g, '');
      input = input.replace(/J/g, 'I');

      console.clear();
      console.log("--- Etapy szyfrowania Bifid ---");

      console.log("1. Oczyszczony tekst:", input);

      let rows = [];
      let cols = [];
      let coordinates = [];
      console.log("2. Współrzędne z tablicy Polybiusa:");
      for (let char of input) {
        const [r, c] = getCoordinates(char);
        rows.push(r);
        cols.push(c);
        console.log(`Litera ${char} → (${r}, ${c})`);
      }
      coordinates = rows.concat(cols);
      
      console.log("3. Lista wszystkich współrzędnych:", coordinates);


      let encrypted = '';
      console.log("4. Tworzenie nowych par i odczyt liter:");
      for (let i = 0; i < coordinates.length; i+=2) {
        const r = coordinates[i];
        const c = coordinates[i+1];
        const letter = getLetter(r, c);
        encrypted += letter;
        console.log(`(${r}, ${c}) → ${letter}`);
      }

      console.log("5. Zaszyfrowana wiadomość:", encrypted);
      document.getElementById("output").textContent = encrypted;
    }

    function decryptBifid() {
      let input = document.getElementById("inputText").value.toUpperCase().replace(/[^A-Z]/g, '');
      input = input.replace(/J/g, 'I');

      console.clear();
      console.log("--- Etapy deszyfrowania Bifid ---");

      console.log("1. Oczyszczony tekst:", input);

      let coordinates = [];
      console.log("2. Współrzędne z tablicy Polybiusa:");
      for (let char of input) {
        const [r, c] = getCoordinates(char);
        coordinates.push(r, c);
        console.log(`Litera ${char} → (${r}, ${c})`);
      }

      console.log("3. Lista wszystkich współrzędnych (r,c,r,c,...):", coordinates);

      const half = coordinates.length / 2;
      const rows = coordinates.slice(0, half);
      const cols = coordinates.slice(half);

      console.log("4. Podzielone współrzędne:");
      console.log("   Nowe wiersze:", rows);
      console.log("   Nowe kolumny:", cols);

      let decrypted = '';
      console.log("5. Tworzenie nowych par i odczyt liter:");
      for (let i = 0; i < rows.length; i++) {
        const r = rows[i];
        const c = cols[i];
        const letter = getLetter(r, c);
        decrypted += letter;
        console.log(`(${r}, ${c}) → ${letter}`);
      }

      console.log("6. Odszyfrowana wiadomość:", decrypted);
      document.getElementById("output").textContent = decrypted;
    }
  </script>
</body>
</html>

Quest: Generator haseł JS

Cel: Ulepszenie narzędzia tworzącego mocne, ale łatwe do zapamiętania hasła. Dodatkowo generator ma uczyć, jak takie hasła tworzyć.

Zadanie: Rozwiń mój generator. Uwzględnij wiedzę o dobrych hasłach. Zadbaj by rozumieć kod.

<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="UTF-8">
  <title>Losowe Hasło</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    #haslo { margin-top: 20px; font-weight: bold; font-size: 1.2em; }
    input { margin-bottom: 10px; padding: 5px; width: 300px; display: block; }
    #specjalnePreview { font-style: italic; color: gray; margin-top: -8px; margin-bottom: 10px; }
  </style>
</head>
<body>

  <input type="text" id="sentencja" placeholder="Wpisz swoją ulubioną sentencję">

  <input type="text" id="liczba" placeholder="Wpisz swoją ulubioną liczbę">
  <div id="specjalnePreview">Znaki specjalne: <span id="znakiSpecjalne"></span></div>
  <input type="text" id="separator" placeholder="Wpisz swój ulubiony separator">

  <button onclick="generujHaslo()">Generuj</button>
  <div id="haslo">Hasło dla Ciebie: <span id="wynik"></span></div>

  <script>
    let elementy = ["kawa", "zupa", "rower", "los", "nic"];

    const mapaSpecjalnychZnaków = {
      "0": ")", "1": "!", "2": "@", "3": "#", "4": "$",
      "5": "%", "6": "^", "7": "&", "8": "*", "9": "("
    };

    function generujZPierwszychLiter(sentencja) {
      return sentencja
        .split(" ")
        .filter(s => s.length > 0)
        .map(s => s[0])
        .join("");
    }

    function zamienLiczbeNaZnakiSpecjalne(liczbaStr) {
      return liczbaStr
        .split("")
        .map(cyfra => mapaSpecjalnychZnaków[cyfra] || "")
        .join("");
    }

    document.getElementById("liczba").addEventListener("input", () => {
      const liczba = document.getElementById("liczba").value.trim();
      const znaki = zamienLiczbeNaZnakiSpecjalne(liczba);
      document.getElementById("znakiSpecjalne").textContent = znaki;
    });

    function generujHaslo() {
      const tekst = document.getElementById("sentencja").value.trim();
      const liczba = document.getElementById("liczba").value.trim();
      const separator = document.getElementById("separator").value.trim();

      if (tekst.length > 0) {
        const skrót = generujZPierwszychLiter(tekst);
        elementy[0] = skrót;
      }

      if (liczba.length > 0 && /^\d+$/.test(liczba)) {
        elementy[1] = liczba;
        elementy[2] = zamienLiczbeNaZnakiSpecjalne(liczba);
      }

      const kopia = [...elementy];
      for (let i = kopia.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [kopia[i], kopia[j]] = [kopia[j], kopia[i]];
      }

      const wybrane = kopia.slice(0, 3);
      const haslo = wybrane.join(separator);
      document.getElementById("wynik").textContent = haslo;
    }
  </script>

</body>
</html>

Quest: WordPress + JS

Przetestuj działanie skryptu, a następnie uruchom go w Wodrpresie.

  • Kod HTML wyklikaj w edytorze WordPressa
  • Kod CSS dodaj do kodu szablonu strony lub za pomocą wtyczki (np. Simple Custom CSS and JS)
  • Kod JS dodaj za pomocą wtyczki (np. CodeSnippets czy Simple Custom CSS and JS)
<style>
  .grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    width: 300px;
  }

  .grid div {
    height: 100px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px solid;
    transition: filter 0.2s;
  }

  .pk-czerwony {
    background-color: red;
    color: white;
    border-color: darkred;
  }

  .pk-niebieski {
    background-color: blue;
    color: white;
    border-color: navy;
  }

  .pk-zielony {
    background-color: green;
    color: white;
    border-color: darkgreen;
  }

  /* Dodaj więcej klas pk- jeśli chcesz */
</style>

<div class="grid">
  <div class="pk-czerwony">1</div>
  <div class="pk-niebieski">2</div>
  <div class="pk-zielony">3</div>
  <div class="pk-czerwony">4</div>
  <div class="pk-niebieski">5</div>
  <div class="pk-zielony">6</div>
  <div class="pk-czerwony">7</div>
  <div class="pk-niebieski">8</div>
  <div class="pk-zielony">9</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
  const colors = [
  "#FFFF00", "#FFE600", "#FFCC00", "#FFB300", "#FF9900",
  "#FF8000", "#FF6600", "#FF4C00", "#FF3300", "#FF1919",
  "#FF0033", "#FF0066", "#FF0099", "#FF00CC", "#F000FF",
  "#CC00FF", "#9900FF", "#6600FF", "#3300FF", "#1900FF",
  "#0033FF", "#0066FF", "#0099FF", "#00CCFF", "#00E6FF",
  "#00FFFF", "#00FFE6", "#00FFCC", "#00FFB3", "#00FF99",
  "#00FF66", "#00FF33", "#00FF00", "#1AFF00", "#33FF00",
  "#4CFF00", "#66FF00", "#80FF00", "#99FF00", "#B3FF00",
  "#CCFF00", "#E6FF00", "#D4E600", "#B3CC00", "#809900",
  "#666699", "#4C4C99", "#333366", "#1A1A66", "#000066"
];

  document.body.addEventListener("wheel", function (e) {
    const target = e.target;

    if (!(target instanceof HTMLElement)) return;

    const pkClass = Array.from(target.classList).find(cls => cls.startsWith("pk-"));
    if (!pkClass) return;

    e.preventDefault();

    // Ustaw domyślny indeks, jeśli jeszcze nie istnieje
    if (!target.dataset.colorIndex) {
      target.dataset.colorIndex = "0";
    }

    let index = parseInt(target.dataset.colorIndex);
    const direction = e.deltaY > 0 ? 1 : -1;

    // Przejście do następnego lub poprzedniego koloru z zawinięciem
    index = (index + direction + colors.length) % colors.length;

    target.dataset.colorIndex = index;
    target.style.backgroundColor = colors[index];
  }, { passive: false });
});
</script>