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
11 changes: 5 additions & 6 deletions layouts/shortcodes/staticsearch.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{{/* {{ partial "_shared/banner.html" . }} */}}
<p id="loading">Loading search data...</p>
<label for="searchBox">Enter full keywords below to find relevant resources for you or use the filters below:</label>
<input disabled placeholder="Enter search text" type="text" name="searchBox" id="searchBox" class="w-100" />
<div id="results"></div>
<script src="{{"/js/search.js" | urlize | relURL }}"></script>
<div class="project-search-box mb-3">
<label for="projectSearch">Search resources by keyword:</label>
<input type="search" id="projectSearch" class="project-search form-control" placeholder="Search resources..." autocapitalize="off" autocomplete="off" spellcheck="false">
<small class="search-count text-muted"></small>
</div>
43 changes: 37 additions & 6 deletions static/js/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,22 @@ var searchFn = function () {
"of", "at", "by", "for", "with", "to", "then", "no", "not",
"so", "too", "can", "and", "but"];
var normalizer = document.createElement("textarea");
// Content normalize: "pre-registration" → " pre registration preregistration "
var normalize = function (input) {
normalizer.innerHTML = input;
var inputDecoded = normalizer.value;
return " " + inputDecoded.trim().toLowerCase().replace(/[^0-9a-z ]/gi, " ").replace(/\s+/g, " ") + " ";
var text = inputDecoded.trim().toLowerCase();
var withSpaces = text.replace(/-/g, " ");
var joined = (text.match(/\w+-[\w-]+/g) || []).map(function (w) { return w.replace(/-/g, ""); }).join(" ");
return " " + (withSpaces + " " + joined).replace(/[^0-9a-z ]/gi, " ").replace(/\s+/g, " ") + " ";
}
var limit = 30;
// Query normalize: strip hyphens so "pre-registration" → " preregistration "
var normalizeQuery = function (input) {
normalizer.innerHTML = input;
var inputDecoded = normalizer.value;
return " " + inputDecoded.trim().toLowerCase().replace(/-/g, "").replace(/[^0-9a-z ]/gi, " ").replace(/\s+/g, " ") + " ";
}
var limit = 500;
var minChars = 2;
var searching = false;
var render = function (results) {
Expand Down Expand Up @@ -41,10 +51,22 @@ var searchFn = function () {
});
return weightResult;
};
var search = function (terms) {
var search = function (terms, requiredWords) {
var results = [];
searchHost.index.forEach(function (item) {
if (item.tags) {
// AND logic: all query words must appear somewhere in the item
var allText = item.title + item.subtitle + item.description + item.content;
item.tags.forEach(function (tag) { allText += tag; });
var allMatch = true;
for (var w = 0; w < requiredWords.length; w++) {
if (!~allText.indexOf(requiredWords[w])) {
allMatch = false;
break;
}
}
if (!allMatch) return;

var weight_1 = 0;
terms.forEach(function (term) {
if (item.title.startsWith(term.term)) {
Expand Down Expand Up @@ -82,7 +104,7 @@ var searchFn = function () {
if (searching) {
return;
}
var term = normalize($("#searchBox").val()).trim();
var term = normalizeQuery($("#searchBox").val()).trim();
if (term === lastTerm) {
return;
}
Expand All @@ -107,14 +129,23 @@ var searchFn = function () {
}
var newTerm = str.trim();
if (newTerm.length >= minChars && stopwords.indexOf(newTerm) < 0) {
var isPrefix = (j === terms.length - 1);
termsTree.push({
weight: weight,
term: " " + str.trim() + " "
term: " " + str.trim() + (isPrefix ? "" : " ")
});
}
}
}
search(termsTree);
// Build required words for AND logic (each query word must appear)
var requiredWords = [];
for (var r = 0; r < terms.length; r++) {
if (terms[r].length >= minChars && stopwords.indexOf(terms[r]) < 0) {
var isLast = (r === terms.length - 1);
requiredWords.push(" " + terms[r] + (isLast ? "" : " "));
}
}
search(termsTree, requiredWords);
searching = false;
var endSearch = new Date();
$("#results").append("<p><small>Search took " + (endSearch - startSearch) + "ms.</small></p>");
Expand Down
44 changes: 41 additions & 3 deletions themes/academic/assets/js/academic.js
Original file line number Diff line number Diff line change
Expand Up @@ -648,20 +648,58 @@
}

$container.imagesLoaded(function () {
let projectFilter = $section.find('.default-project-filter').text();
let projectSearchTerms = null;

// Initialize Isotope after all images have loaded.
$container.isotope({
itemSelector: '.isotope-item',
layoutMode: layout,
masonry: {
gutter: 20
},
filter: $section.find('.default-project-filter').text()
filter: function () {
let $this = $(this);
let filterMatch = projectFilter ? $this.is(projectFilter) : true;
if (!filterMatch) return false;
if (!projectSearchTerms) return true;
let text = $this.text().replace(/-/g, '');
return projectSearchTerms.every(function (re) { return re.test(text); });
}
});

// Text search on cards.
let searchTimeout;
let $searchCount = $section.find('.search-count');
$section.find('.project-search').keyup(function () {
clearTimeout(searchTimeout);
let input = this;
searchTimeout = setTimeout(function () {
let val = $(input).val().trim();
if (val) {
// Split on hyphens/spaces, require all words (AND logic, prefix matching)
let words = val.replace(/-/g, ' ').split(/\s+/).filter(function (w) { return w.length >= 2; });
projectSearchTerms = words.length ? words.map(function (w) {
return new RegExp(w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i');
}) : null;
} else {
projectSearchTerms = null;
}
$container.isotope();
let count = $container.isotope('getFilteredItemElements').length;
let total = $container.find('.isotope-item').length;
if (projectSearchTerms) {
$searchCount.text(count + ' of ' + total + ' resources shown');
} else {
$searchCount.text('');
}
}, 200);
});

// Filter items when filter link is clicked.
$section.find('.project-filters a').click(function () {
let selector = $(this).attr('data-filter');
$container.isotope({filter: selector});
projectFilter = $(this).attr('data-filter');
$container.isotope();
$(this).removeClass('active').addClass('active').siblings().removeClass('active all');
return false;
});
Expand Down
Loading