Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/assets/css/reprodb-search.css
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,25 @@ html { overflow-y: scroll; }
.rdb-icon-btn svg {
display: block;
}
.rdb-help-link {
display: inline-flex;
align-items: center;
justify-content: center;
height: 28px;
padding: 0 9px;
border: 1px solid #ccc;
border-radius: 4px;
background: #fff;
color: #555;
font-size: 0.82em;
font-weight: 600;
text-decoration: none;
transition: background 0.15s, color 0.15s;
}
.rdb-help-link:hover {
background: #f0f0f0;
color: #333;
}

/* ── Profile cards (author/institution cards in search results) ─────── */
#profileCards {
Expand Down Expand Up @@ -119,6 +138,8 @@ html { overflow-y: scroll; }
html:not([data-theme="light"]) #filters select { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; }
html:not([data-theme="light"]) .rdb-icon-btn { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; }
html:not([data-theme="light"]) .rdb-icon-btn:hover { background: #2a3038; color: #fff; }
html:not([data-theme="light"]) .rdb-help-link { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; }
html:not([data-theme="light"]) .rdb-help-link:hover { background: #2a3038; color: #fff; }
html:not([data-theme="light"]) #pagination button { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; }
html:not([data-theme="light"]) .profile-card { background: #23272d; border-color: #4a4f57; color: #d6d9dc; }
html:not([data-theme="light"]) .profile-card:hover { border-color: #5a9cf5; }
Expand All @@ -134,6 +155,8 @@ html { overflow-y: scroll; }
html[data-theme="dark"] #filters select { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; }
html[data-theme="dark"] .rdb-icon-btn { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; }
html[data-theme="dark"] .rdb-icon-btn:hover { background: #2a3038; color: #fff; }
html[data-theme="dark"] .rdb-help-link { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; }
html[data-theme="dark"] .rdb-help-link:hover { background: #2a3038; color: #fff; }
html[data-theme="dark"] #pagination button { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; }
html[data-theme="dark"] .profile-card { background: #23272d; border-color: #4a4f57; color: #d6d9dc; }
html[data-theme="dark"] .profile-card:hover { border-color: #5a9cf5; }
Expand Down
55 changes: 46 additions & 9 deletions src/assets/js/reprodb-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,34 @@
return (s || '').toLowerCase().replace(/[^a-z0-9 ]/g, ' ');
}

function normalizeBadgeForFilter(v) {
var t = String(v || '').toLowerCase().replace('badges: ', '').trim();
if (t === 'reproducible') return 'reproduced';
if (t === 'artifact evaluated') return 'evaluated';
return t;
}

function extractBadgeKeywords(raw) {
var matches = String(raw || '').toLowerCase().match(/#[a-z]+/g) || [];
var seen = {};
var selected = [];
matches.forEach(function(tag) {
var t = tag.replace(/^#/, '');
if (t === 'reproducible') t = 'reproduced';
if (['available', 'reproduced', 'functional', 'reusable', 'evaluated'].indexOf(t) !== -1 && !seen[t]) {
seen[t] = true;
selected.push(t);
}
});
return selected;
}

function stripMagicKeywords(raw) {
return String(raw || '')
.replace(/#(unavailable|awarded|github|zenodo|nourl|artifinder|available|reproduced|reproducible|functional|reusable|evaluated)/gi, '')
.trim();
}

function buildSearchIndex(data) {
data.forEach(function(d) {
d._search = normalizeText(d.title) + ' ' +
Expand Down Expand Up @@ -62,14 +90,16 @@

function doSearch() {
var raw = document.getElementById('searchBox').value.trim();
var rawLower = raw.toLowerCase();
// Parse magic keywords
var onlyUnavail = raw.indexOf('#unavailable') !== -1;
var onlyAwarded = raw.indexOf('#awarded') !== -1;
var onlyGithub = raw.indexOf('#github') !== -1;
var onlyZenodo = raw.indexOf('#zenodo') !== -1;
var onlyNourl = raw.indexOf('#nourl') !== -1;
var onlyArtifinder = raw.indexOf('#artifinder') !== -1;
var cleaned = raw.replace(/#(unavailable|awarded|github|zenodo|nourl|artifinder)/g, '').trim();
var onlyUnavail = rawLower.indexOf('#unavailable') !== -1;
var onlyAwarded = rawLower.indexOf('#awarded') !== -1;
var onlyGithub = rawLower.indexOf('#github') !== -1;
var onlyZenodo = rawLower.indexOf('#zenodo') !== -1;
var onlyNourl = rawLower.indexOf('#nourl') !== -1;
var onlyArtifinder = rawLower.indexOf('#artifinder') !== -1;
var selectedBadges = extractBadgeKeywords(rawLower);
var cleaned = stripMagicKeywords(raw);
var query = normalizeText(cleaned);
var yearVal = document.getElementById('yearFilter').value;
var venueVal = document.getElementById('venueFilter').value;
Expand Down Expand Up @@ -106,6 +136,13 @@
if (onlyArtifinder) {
if (String(d.source || '').toLowerCase() !== 'artifinder') return false;
}
if (selectedBadges.length > 0) {
var recBadges = (d.badges || []).map(normalizeBadgeForFilter);
var hasAllSelectedBadges = selectedBadges.every(function(b) {
return recBadges.indexOf(b) !== -1;
});
if (!hasAllSelectedBadges) return false;
}
if (terms.length === 0) return true;
return terms.every(function(t) { return d._search.indexOf(t) !== -1; });
});
Expand Down Expand Up @@ -175,7 +212,7 @@
var status = document.getElementById('searchStatus');
var hero = document.getElementById('search-hero');
var query = document.getElementById('searchBox').value.trim();
var cleaned = query.replace(/#(unavailable|awarded|github|zenodo|nourl|artifinder)/g, '').trim();
var cleaned = stripMagicKeywords(query);
var terms = normalizeText(cleaned).split(/\s+/).filter(function(t) { return t.length > 0; });
var yearVal = document.getElementById('yearFilter').value;
var venueVal = document.getElementById('venueFilter').value;
Expand Down Expand Up @@ -514,7 +551,7 @@
// Re-render profile cards if the user is already searching
if (filtered.length > 0 || document.getElementById('searchBox').value.trim().length >= 2) {
var raw = document.getElementById('searchBox').value.trim();
var cleaned = raw.replace(/#(unavailable|awarded|github|zenodo|nourl|artifinder)/g, '').trim();
var cleaned = stripMagicKeywords(raw);
var terms = normalizeText(cleaned).split(/\s+/).filter(function(t) { return t.length > 0; });
renderProfileCards(raw, terms);
}
Expand Down
3 changes: 3 additions & 0 deletions src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ skip_chartjs: true
<button id="shareBtn" class="rdb-icon-btn" onclick="shareSearch()" title="Copy search link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
</button>
<a id="searchHelpLink" class="rdb-help-link" href="{{ '/methodology/search-keywords.html' | relative_url }}" title="Search keywords" aria-label="Search keywords help">
<i class="fas fa-hashtag" aria-hidden="true"></i>
</a>
</span>
</div>
</div>
Expand Down
8 changes: 8 additions & 0 deletions src/methodology/search-keywords.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ The [search bar]({{ '/' | relative_url }}) supports special `#` keywords that fi
| `#zenodo` | Artifacts hosted on Zenodo |
| `#nourl` | Artifacts with no artifact URL recorded |
| `#artifinder` | Results tagged as ArtiFinder-discovered only |
| `#available` | Artifacts with the Available badge |
| `#functional` | Artifacts with the Functional badge |
| `#reproduced` | Artifacts with the Reproduced badge |
| `#reproducible` | Alias for `#reproduced` |
| `#reusable` | Artifacts with the Reusable badge |
| `#evaluated` | Artifacts with the Artifact Evaluated badge |

**Examples:**

Expand All @@ -22,6 +28,8 @@ The [search bar]({{ '/' | relative_url }}) supports special `#` keywords that fi
- `#github #unavailable 2022` - GitHub-hosted artifacts from 2022 with dead links
- `#zenodo fuzzing` - Zenodo-hosted fuzzing artifacts
- `#artifinder malware` - ArtiFinder-tagged artifacts matching "malware"
- `#available #functional` - artifacts that are both available and functional
- `#reproducible usenixsec` - reproduced artifacts for USENIX Security

Keywords also work alongside the year, venue, and area dropdown filters.

Expand Down