Kris
·
09 Aug 23
client.js
1function addUrlParam(key, value) {
2 if (history.pushState) {
3 const searchParams = new URLSearchParams(window.location.search);
4 searchParams.set(key, value);
5 const newurl =
6 window.location.protocol +
7 "//" +
8 window.location.host +
9 window.location.pathname +
10 "?" +
11 searchParams.toString();
12 window.history.pushState({ path: newurl }, "", newurl);
13 }
14}
15
16function rmUrlParam(key) {
17 const url = window.location.href;
18 const r = new URL(url);
19 r.searchParams.delete(key);
20 const newUrl = r.href;
21 window.history.pushState({ path: newUrl }, "", newUrl);
22}
23
24function getUrlParam(key) {
25 const url = window.location.href;
26 const r = new URL(url);
27 return r.searchParams.get(key);
28}
29
30function createFilter(pluginsEl) {
31 function filter(value) {
32 function each(el) {
33 if (!value) {
34 el.classList.remove("hidden");
35 return;
36 }
37 const search = value.toLocaleLowerCase();
38 const username = el.dataset.username;
39 const repo = el.dataset.repo;
40 const desc = el.dataset.desc || "";
41 const tags = el.dataset.tags.split(",");
42
43 let hasTags = false;
44 if (search.includes("tag:")) {
45 const nextSearch = search.replace("tag:", "");
46 hasTags = tags.some((tag) => tag === nextSearch);
47 }
48
49 const showEl = username.includes(search)
50 || repo.includes(search)
51 || desc.includes(search)
52 || hasTags;
53
54 if (showEl) {
55 el.classList.remove("hidden");
56 } else {
57 el.classList.add("hidden");
58 }
59 }
60
61 pluginsEl.forEach(each);
62 }
63
64 return filter;
65}
66
67function updateLinks(search) {
68 const links = must("#sort_links");
69 for (let link of links.children) {
70 const r = new URL(link.href);
71 if (search) {
72 r.searchParams.set("search", search);
73 } else {
74 r.searchParams.delete("search");
75 }
76 const newUrl = r.href;
77 link.href = newUrl;
78 }
79}
80
81function must(pattern) {
82 const el = document.querySelector(pattern);
83 if (!el) {
84 throw new Error(`${pattern} not found`);
85 }
86 return el;
87}
88
89function mustAll(pattern) {
90 const el = document.querySelectorAll(pattern);
91 if (!el) {
92 throw new Error(`${pattern} not found`);
93 }
94 return el;
95}
96
97function debounce(fn, delay) {
98 let timeoutID;
99 return function(...args) {
100 if (timeoutID) {
101 clearTimeout(timeoutID);
102 }
103 timeoutID = setTimeout(() => {
104 fn(...args);
105 }, delay);
106 };
107}
108
109function init() {
110 const searchEl = must("#search");
111 const clearSearchEl = must("#search_clear");
112 const pluginsEl = mustAll(".plugin");
113 const tagsEl = mustAll(".tag");
114
115 const urlParam = getUrlParam("search");
116 searchEl.value = urlParam;
117 const filter = createFilter(pluginsEl);
118
119 const search = (value) => {
120 if (value) {
121 addUrlParam("search", value);
122 updateLinks(value);
123 } else {
124 rmUrlParam("search");
125 updateLinks("");
126 }
127
128 filter(value);
129 };
130
131 const debouncedSearch = debounce((ev) => {
132 const value = ev.target.value;
133 search(value);
134 }, 150);
135
136 searchEl.addEventListener("input", debouncedSearch);
137
138
139 clearSearchEl.addEventListener("click", () => {
140 searchEl.value = "";
141 rmUrlParam("search");
142 updateLinks("");
143 filter("");
144 });
145
146 tagsEl.forEach((tagEl) => {
147 tagEl.addEventListener("click", (el) => {
148 const id = el.target.dataset.id;
149 if (!id) return;
150 const value = `tag:${id}`;
151 searchEl.value = value;
152 search(value);
153 });
154 });
155 filter(searchEl.value);
156}
157
158document.addEventListener("DOMContentLoaded", init);