Webbserverprogrammering 1

Show sourcecode

The following files exists in this folder. Click to view.

webbserverprogrammering/submissions/projekt-matkort-handler/public/js/

map.js
roulette.js

roulette.js

178 lines UTF-8 Windows (CRLF)
$(document).ready(function () {
  let rouletteItems = [];
  const colors = [
    "#f44336",
    "#2196f3",
    "#4caf50",
    "#ff9800",
    "#9c27b0",
    "#00bcd4",
    "#e91e63",
  ];

  // Map `restaurants` var (comes from index.php script tag) to searchable list
  const autocompleteList = restaurants.map((r) => ({
    id: r.id,
    name: r.name,
    address: r.address,
  }));

  // Toggle Sidebar
  $("#toggle-roulette-btn").click(function () {
    $("#roulette-sidebar").addClass("active");
    $(this).fadeOut(200); // Hide the toggle button when roulette opens
  });

  $("#close-roulette-btn").click(function () {
    $("#roulette-sidebar").removeClass("active");
    $("#toggle-roulette-btn").fadeIn(200); // Show the toggle button again when it closes
  });

  function updateRouletteUI() {
    const listEl = $("#roulette-items-list");
    listEl.empty();

    if (rouletteItems.length === 0) {
      $("#roulette-wheel").html(
        '<div class="wheel-placeholder">Välj matställen...</div>',
      );
      $("#roulette-wheel").css("background", "var(--bg-color)"); // Use theme var instead of white/gray #1
      $("#spin-btn").prop("disabled", true);
      return;
    }

    $("#spin-btn").prop("disabled", false);

    // Populate list
    rouletteItems.forEach((item, index) => {
      listEl.append(`
                <li style="padding: 6px 8px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center;">
                    <span style="font-size: 0.9rem;">${item.name}</span>
                    <button class="remove-item" data-id="${item.id}" type="button" style="border: none; background: none; color: var(--secondary); font-weight: bold; cursor: pointer; font-size: 1.2rem; line-height: 1;">&times;</button>
                </li>
            `);
    });

    // Draw Wheel (CSS Conic Gradient)
    let gradientStops = [];
    const slicePercent = 100 / rouletteItems.length;

    rouletteItems.forEach((item, index) => {
      const color = colors[index % colors.length];
      const startStr = `${slicePercent * index}%`;
      const endStr = `${slicePercent * (index + 1)}%`;
      gradientStops.push(`${color} ${startStr} ${endStr}`);
    });

    $("#roulette-wheel").empty();
    $("#roulette-wheel").css(
      "background",
      `conic-gradient(${gradientStops.join(", ")})`,
    );
  }

  function addRestaurant(restObj) {
    if (!rouletteItems.find((r) => r.id === restObj.id)) {
      rouletteItems.push(restObj);
      updateRouletteUI();
    }
  }

  // --- Events ---

  // Autocomplete Search
  $("#roulette-search").on("keyup input", function () {
    const val = $(this).val().toLowerCase();
    const resBox = $("#roulette-search-result");
    resBox.empty();

    if (val.length > 0) {
      const matches = autocompleteList
        .filter((r) => r.name.toLowerCase().includes(val))
        .slice(0, 5);
      matches.forEach((m) => {
        resBox.append(
          `<p data-id="${m.id}" data-name="${m.name}">${m.name}</p>`,
        );
      });
    }
  });

  $(document).on("click", "#roulette-search-result p", function () {
    const id = $(this).data("id");
    const name = $(this).data("name");
    addRestaurant({ id, name });
    $("#roulette-search").val("");
    $("#roulette-search-result").empty();
  });

  // Remove item
  $(document).on("click", ".remove-item", function () {
    const id = $(this).data("id");
    rouletteItems = rouletteItems.filter((r) => r.id !== id);
    updateRouletteUI();
  });

  // Clear all
  $("#clear-roulette-btn").click(function () {
    rouletteItems = [];
    $("#roulette-winner").hide();
    updateRouletteUI();
  });

  // Apply all
  $("#apply-all-btn").click(function () {
    autocompleteList.forEach((r) => addRestaurant(r));
  });

  // Add favorites (Ajax)
  $("#add-favourites-btn").click(function () {
    $.get("api/restaurant_ajax.php?action=get_favorites", function (data) {
      if (data && data.length > 0) {
        data.forEach((fav) => addRestaurant({ id: fav.id, name: fav.name }));
      } else {
        alert("Du har inga sparade favoriter ännu!");
      }
    });
  });

  // Spin!
  let currentRotation = 0;
  $("#spin-btn").click(function () {
    if (rouletteItems.length === 0) return;

    $("#roulette-winner").hide();
    $("#spin-btn").prop("disabled", true);

    // Calculate random slice
    const sliceDegrees = 360 / rouletteItems.length;
    const randomItemIndex = Math.floor(Math.random() * rouletteItems.length);

    // Target angle (where the pointer hits the middle of the selected slice)
    const targetSliceAngle = randomItemIndex * sliceDegrees + sliceDegrees / 2;

    // Number of full spins
    const spins = 5 * 360;

    // Calculate how much we need to rotate to land on the target from the CURRENT rotation
    const currentRotMod = currentRotation % 360;
    const desiredRotMod = 360 - targetSliceAngle;
    let offset = desiredRotMod - currentRotMod;

    if (offset < 0) {
      offset += 360;
    }

    currentRotation += spins + offset;

    $("#roulette-wheel").css("transform", `rotate(${currentRotation}deg)`);

    setTimeout(() => {
      const winner = rouletteItems[randomItemIndex];
      $("#winner-name").text(winner.name);
      $("#roulette-winner").fadeIn();
      $("#spin-btn").prop("disabled", false);
    }, 3000); // Matches CSS transition duration
  });
});