Created
August 20, 2021 10:09
-
-
Save bacor/9508dc0eed6e51229ba389c78bf40f64 to your computer and use it in GitHub Desktop.
Filter for BibBase publications
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* PubFilter | |
* | |
* Author: Bas Cornelissen, based on a script by Floris Roelofsen | |
* Copyright: Bas Cornelissen | |
* Date: 7 august 2021 | |
* Version 0.1 | |
*/ | |
/** | |
* This class allows you to filter a list of BibBase publications | |
* using a form with a search option and several selectors. | |
* | |
* Replacements | |
* ------------ | |
* | |
* BibBase generates references that often contain things like | |
* "Author 1, & Author 2" where you want "Author 1 & Author 2". | |
* To fix this, PubFilter iterates over the leafs of every | |
* publication to replace all such patterns. You can specify | |
* more patterns by passing the option `replacements`: | |
* | |
* PubFilter('myFormId', 'myContainerId', { | |
* replacements: { | |
* 'myRegexPattern': 'replacement' | |
* } | |
* }) | |
* | |
* @param {string} formId The id of the filtering form | |
* @param {string} containerId The id of the container with all publications | |
* @returns null | |
*/ | |
function PubFilter(formId, containerId, options) { | |
if (!(this instanceof PubFilter)) { | |
return new PubFilter(formId, containerId, options); | |
} | |
// Options | |
var defaultOptions = { | |
// Selectors | |
searchSelector: 'input.search', | |
pubGroupSelector: '.bibbase_group_whole', | |
pubSelector: '.bibbase_paper', | |
// Core replacements; please override using the replacements option | |
_coreReplacements: { | |
',\\s+&': ' &', | |
'.,\\s+editor(s)': ', editor(s)' | |
}, | |
// Use this to pass (additional) replacements (same format as above) | |
replacements: {}, | |
// Leafs of the publications to which the replacements are applied | |
replacementSelector: '.bibbase_paper_author, .bibbase_paper_title a, i' | |
}; | |
this.opts = Object.assign({}, defaultOptions, options); | |
this.opts.replacements = Object.assign({}, this.opts._coreReplacements, this.opts.replacements); | |
// Register key elements | |
this.form = $('#' + formId).addClass('pf-form'); | |
this.container = $('#'+ containerId).addClass('pf-container'); | |
this.resetBtn = $.merge(this.form.find('.pf-reset'), this.container.find('.pf-reset')); | |
this.selectors = this.form.find('select'); | |
this.search = this.form.find(this.opts.searchSelector); | |
this.publications = this.container.find(this.opts.pubSelector); | |
this.publicationGroups = this.container.find(this.opts.pubGroupSelector); | |
this.noMatches = this.container.find('.pf-no-matches').addClass('pf-hidden'); | |
// Update the form whenever a selection is changed | |
this.selectors.change(this.update.bind(this)); | |
// Whenever text is entered in the search box, update pubList | |
this.search.keyup(this.update.bind(this)); | |
// Whenever the reset button is clicked, | |
// reset the selection elements and update pubList | |
this.resetBtn.click(this.reset.bind(this)); | |
this.applyReplacements(); | |
this.showCounts(); | |
this.update(); | |
} | |
/** | |
* Applies all replacements specified in the options. The function | |
* iterates over all the leafs of every publication and applies the | |
* replacement to the text of every leaf. | |
*/ | |
PubFilter.prototype.applyReplacements = function() { | |
var elements = this.publications.find(this.opts.replacementSelector); | |
elements.each(function(index, el) { | |
var text = $(el).text(); | |
$.each(this.opts.replacements, function(key, value) { | |
var expr = RegExp(key, 'g'); | |
text = text.replace(expr, value); | |
}) | |
$(el).text(text); | |
}.bind(this)); | |
} | |
/** | |
* Clean up an array of keywords | |
* @param {Array} keywords An array of keywords | |
* @returns trimmed, lowercase keywords | |
*/ | |
PubFilter.prototype.cleanKeywords = function(keywords) { | |
return keywords | |
.filter(function(keyword){ return keyword !== '' }) | |
.map(function(keyword) { | |
return keyword.trim().toLowerCase() | |
}); | |
} | |
/** | |
* Collects all keywords currently entered or selected in the form | |
* | |
* @returns Array of keywords | |
*/ | |
PubFilter.prototype.collectKeywords = function() { | |
// add keywords in the search field | |
var keywords = [this.search.val()]; | |
// add keywords from the selection elements | |
this.selectors.each(function(index, selector) { | |
value = $(selector).val(); | |
if(value === null) return; | |
parts = value.split(/\s*\/\s*/) | |
parts.forEach(function(part) { | |
keywords.push(part); | |
}); | |
}); | |
return this.cleanKeywords(keywords); | |
} | |
/** | |
* Find all publications that match an array of keywords. A publication | |
* matches an array of keywords if any of the keywords occurs anywhere in | |
* the full HTML corresponding of that publication. The full HTML includes | |
* a BibTeX entry, an abstract, etc. | |
* | |
* @param {Array} keywords An array of keywords | |
* @returns An array of publication elements matching the keywords | |
*/ | |
PubFilter.prototype.filterPublications = function(keywords) { | |
// No keywords: all publications match | |
if(keywords.length === 0) return this.publications; | |
// Otherwise filter the publications | |
var matches = this.publications.filter(function(index, pub) { | |
var text = $(pub).text().toLowerCase(); | |
for(var i=0; i <= keywords.length; i++) { | |
if(text.includes(keywords[i])) return true; | |
} | |
return false; | |
}); | |
return matches; | |
} | |
/** | |
* Update the publications shown based on selected options in the form | |
*/ | |
PubFilter.prototype.update = function() { | |
var keywords = this.collectKeywords(); | |
var matches = this.filterPublications(keywords); | |
console.log('Keywords:', keywords); | |
// First hide all publications | |
this.publications.each(function(index, pub) { | |
$(pub).removeClass('pf-visible').addClass("pf-hidden"); | |
}); | |
// Then make the matches visible | |
matches.each(function(index, pub){ | |
$(pub).removeClass('pf-hidden').addClass("pf-visible"); | |
}); | |
// Only show publication groups that contain visible publications | |
this.publicationGroups.each(function(index, group) { | |
var pubs = $(group).find('.bibbase_paper.pf-visible'); | |
if(pubs.length > 0) { | |
$(group).removeClass('pf-hidden'); | |
} else { | |
$(group).addClass('pf-hidden'); | |
} | |
}); | |
// Show message when there are no matches | |
this.noMatches.toggleClass('pf-hidden', matches.length > 0); | |
} | |
/** | |
* Reset the form and publications | |
*/ | |
PubFilter.prototype.reset = function() { | |
this.form[0].reset(); | |
this.update(); | |
} | |
/** | |
* Show the number of matches for every option | |
*/ | |
PubFilter.prototype.showCounts = function() { | |
this.selectors.each(function(index, selector) { | |
var options = $(selector).find('option'); | |
options.each(function(index, option) { | |
var keywords = this.cleanKeywords(option.value.split(/\s*\/\s*/)); | |
var matches = this.filterPublications(keywords); | |
var count = matches.length; | |
if (option.value != "") { | |
option.innerHTML = option.innerHTML + ' (' + count + ')'; | |
} | |
}.bind(this)); | |
}.bind(this)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment