PageSourceSearch

https://www.rstcloud.com/rst/assets/js/rst-post-filter.js

js rstcloud.com collected 2026-10-02 07:19:15 UTC 3,180 bytes, 119 lines download raw bytes

1(function () {
2	'use strict';
3
4	// Same URL shape the old Search Filter Pro plugin used
5	// (?_sft_post_tag=tag-one,tag-two) so existing shared/bookmarked links
6	// keep working unchanged.
7	function buildUrl(form) {
8		var params = new URLSearchParams(window.location.search);
9		params.delete('_sft_post_tag');
10
11		var values = Array.from(form.querySelectorAll('input[name="rst_tag[]"]:checked')).map(function (cb) {
12			return cb.value;
13		});
14		if (values.length) {
15			params.set('_sft_post_tag', values.join(','));
16		}
17
18		var query = params.toString();
19		return window.location.pathname + (query ? '?' + query : '');
20	}
21
22	function swapByNode(newDoc, nodeId) {
23		[
24			'.rst-post-filter[data-rst-filter-for="' + nodeId + '"]',
25			'[data-node="' + nodeId + '"]'
26		].forEach(function (selector) {
27			var oldEl = document.querySelector(selector);
28			var newEl = newDoc.querySelector(selector);
29			if (oldEl && newEl) {
30				oldEl.replaceWith(newEl);
31			}
32		});
33	}
34
35	var inflight = null;
36
37	function fetchAndSwap(url, nodeId, loadingEl) {
38		if (loadingEl) {
39			loadingEl.classList.add('is-loading');
40		}
41
42		// Cancel any in-flight request so rapid pill clicks can't apply
43		// their responses out of order.
44		if (inflight) {
45			inflight.abort();
46		}
47		var controller = new AbortController();
48		inflight = controller;
49
50		return fetch(url, {
51			headers: { 'X-Requested-With': 'XMLHttpRequest' },
52			signal: controller.signal
53		})
54			.then(function (res) {
55				if (!res.ok) {
56					throw new Error('Request failed');
57				}
58				return res.text();
59			})
60			.then(function (html) {
61				inflight = null;
62				var doc = new DOMParser().parseFromString(html, 'text/html');
63				swapByNode(doc, nodeId);
64				window.history.pushState({}, '', url);
65			})
66			.catch(function (err) {
67				if (err && err.name === 'AbortError') {
68					return;
69				}
70				window.location.href = url;
71			});
72	}
73
74	function onFilterChange(e) {
75		var form = e.target.closest('.rst-post-filter');
76		if (!form) {
77			return;
78		}
79		if (!e.target.matches('input[name="rst_tag[]"]')) {
80			return;
81		}
82		var nodeId = form.getAttribute('data-rst-filter-for');
83		fetchAndSwap(buildUrl(form), nodeId, form);
84	}
85
86	function onFilterSubmit(e) {
87		var form = e.target.closest('.rst-post-filter');
88		if (!form) {
89			return;
90		}
91		e.preventDefault();
92		var nodeId = form.getAttribute('data-rst-filter-for');
93		fetchAndSwap(buildUrl(form), nodeId, form);
94	}
95
96	function onPaginationClick(e) {
97		var link = e.target.closest('.page-numbers');
98		if (!link || !link.getAttribute('href') || link.classList.contains('dots')) {
99			return;
100		}
101		var wrapper = link.closest('[data-node]');
102		if (!wrapper) {
103			return;
104		}
105		e.preventDefault();
106		var nodeId = wrapper.getAttribute('data-node');
107		fetchAndSwap(link.href, nodeId, wrapper).then(function () {
108			var el = document.querySelector('[data-node="' + nodeId + '"]');
109			if (el) {
110				el.scrollIntoView({ behavior: 'smooth', block: 'start' });
111			}
112		});
113	}
114
115	// Everything is delegated on document so handlers survive AJAX swaps.
116	document.addEventListener('change', onFilterChange);
117	document.addEventListener('submit', onFilterSubmit);
118	document.addEventListener('click', onPaginationClick);
119})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.