1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// Axis tags: Enter turns what was typed into a tag, × removes one. See
// lib/rank_king/views/axis_tags.rb for the markup this expects.
(function () {
  "use strict";

  document.addEventListener("keydown", function (event) {
    var input = event.target;
    if (event.key !== "Enter" || !input.closest || !input.closest("[data-tags] [data-dashed]")) return;

    event.preventDefault();
    var name = input.value.trim();
    if (!name) return;

    var tag = document.createElement("span");
    tag.className = "tag";
    tag.append(name);

    var hidden = document.createElement("input");
    hidden.type = "hidden";
    hidden.name = "axes[]";
    hidden.value = name;

    var remove = document.createElement("button");
    remove.type = "button";
    remove.setAttribute("aria-label", "Remove " + name);
    remove.textContent = "×";

    tag.append(hidden, remove);
    input.closest("[data-dashed]").before(tag);
    input.value = "";
  });

  document.addEventListener("click", function (event) {
    var remove = event.target.closest("[data-tags] .tag button");
    if (remove) remove.closest(".tag").remove();
  });

  // A destructive form asks first: <form data-confirm="Delete this list?">.
  document.addEventListener("submit", function (event) {
    var message = event.target.dataset.confirm;
    if (message && !window.confirm(message)) event.preventDefault();
  });

  // A slow form shows it is working: <form data-busy="Syncing…"> relabels
  // the button that sent it and marks it busy until the next page loads.
  document.addEventListener("submit", function (event) {
    var label = event.target.dataset.busy;
    var button = event.submitter;
    if (!label || !button || event.defaultPrevented) return;

    button.textContent = label;
    button.setAttribute("aria-busy", "true");
    setTimeout(function () { button.disabled = true; });
  });

  // The new-list form shows only the chosen source's fields. Disabling
  // the others keeps their required fields from blocking the submit.
  function showSource(select) {
    select.form.querySelectorAll("fieldset[data-source]").forEach(function (fieldset) {
      var chosen = fieldset.dataset.source === select.value;
      fieldset.disabled = !chosen;
      fieldset.hidden = !chosen;
    });
  }

  document.addEventListener("change", function (event) {
    if (event.target.matches("select[name=source]")) showSource(event.target);
  });

  // A pasted YouTube cookie fetches its playlists into the form's
  // [data-playlists], which the server draws as a fragment.
  var playlistsTimer;
  document.addEventListener("input", function (event) {
    var cookie = event.target;
    var fieldset = cookie.name === "cookie" && cookie.closest("fieldset");
    var target = fieldset && fieldset.querySelector("[data-playlists]");
    if (!target) return;

    clearTimeout(playlistsTimer);
    playlistsTimer = setTimeout(function () {
      if (!cookie.value.trim()) return;

      var body = new FormData();
      body.append("cookie", cookie.value);
      body.append("_csrf", target.dataset.csrf);
      target.setAttribute("aria-busy", "true");
      fetch(target.dataset.playlists, { method: "POST", body: body })
        .then(function (response) { return response.text(); })
        .then(function (html) { target.innerHTML = html; })
        .catch(function () { target.textContent = "Could not reach rank-king to list your playlists."; })
        .finally(function () { target.removeAttribute("aria-busy"); });
    }, 300);
  });

  document.addEventListener("DOMContentLoaded", function () {
    document.querySelectorAll("select[name=source]").forEach(showSource);
  });
})();