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.